Skip to content

Instantly share code, notes, and snippets.

@rishubil
Last active October 14, 2022 18:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rishubil/add77c01e174a325cc87897c7ce24ecd to your computer and use it in GitHub Desktop.
Save rishubil/add77c01e174a325cc87897c7ce24ecd to your computer and use it in GitHub Desktop.
스플래툰에서 팀을 바꾸는 좋은 전략이 있을까?
"""
스플래툰3에서 팀을 바꾸는 좋은 전략이 있을까?
---
목표: 팀을 바꿀지 말지 결정할 때, 더 승률이 높은 전략을 찾는다.
조건:
- 기본적으로 스플래툰3의 카오폴리스 매치 (챌린지)를 기준으로 한다.
- 게임에서 획득한 금메달, 은메달로 얻을 수 있는 추가 포인트는 무시한다.
- 랭크 포인트 상한, 하한은 원래 9999, -9999이지만, 이를 무시한다.
- 모든 플레이어는 동시에 매치메이킹을 한다.
- 각 팀의 승률은 팀의 평균 실력(0.0 ~ 1.0)의 차이를 바탕으로 (여러 가지 방법으로) 계산한다.
"""
import math
import random
from tqdm import tqdm
def ease_in_out_sine(t: float) -> float:
return (1 - math.cos(math.pi * t)) / 2
def ease_in_out_cubic(t: float) -> float:
return 4 * t**3 if t < 0.5 else 1 - math.pow(-2 * t + 2, 3) / 2
def ease_in_out_quint(t: float) -> float:
return 16 * t**5 if t < 0.5 else 1 - math.pow(-2 * t + 2, 5) / 2
class RankGroup:
win_additional_point_list = [0, 0, 5, 15, 30, 50]
def __init__(
self,
name: str,
index: int,
init_point: int,
rank_group_up_point: int,
win_point_base: int,
) -> None:
self.name = name
self.index = index
self.init_point = init_point
self.rank_group_up_point = rank_group_up_point
self.win_point_base = win_point_base
def __repr__(self) -> str:
return self.name
def calc_win_point(self, win_count: int) -> int:
return (
self.win_point_base * win_count + self.win_additional_point_list[win_count]
)
class Rank:
def __init__(
self,
name: str,
index: int,
group: RankGroup,
rank_up_point: int,
enter_fee: int,
) -> None:
self.name = name
self.index = index
self.group = group
self.rank_up_point = rank_up_point
self.enter_fee = enter_fee
def __repr__(self) -> str:
return self.name
RANK_GROUPS = [
RankGroup(
name="C Group",
index=0,
init_point=10,
rank_group_up_point=600,
win_point_base=20,
),
RankGroup(
name="B Group",
index=1,
init_point=100,
rank_group_up_point=850,
win_point_base=30,
),
RankGroup(
name="A Group",
index=2,
init_point=200,
rank_group_up_point=1100,
win_point_base=40,
),
RankGroup(
name="S Group",
index=3,
init_point=300,
rank_group_up_point=100,
win_point_base=50,
),
RankGroup(
name="S+0 Group",
index=4,
init_point=300,
rank_group_up_point=3800,
win_point_base=50,
),
RankGroup(
name="S+10 Group",
index=5,
init_point=300,
rank_group_up_point=3800,
win_point_base=50,
),
RankGroup(
name="S+20 Group",
index=6,
init_point=300,
rank_group_up_point=3800,
win_point_base=50,
),
RankGroup(
name="S+30 Group",
index=7,
init_point=300,
rank_group_up_point=3800,
win_point_base=50,
),
RankGroup(
name="S+40 Group",
index=8,
init_point=300,
rank_group_up_point=3800,
win_point_base=50,
),
RankGroup(
name="S+50 Group",
index=9,
init_point=300,
rank_group_up_point=9999,
win_point_base=50,
),
]
LAST_RANK_GROUP_INDEX = len(RANK_GROUPS) - 1
RANKS = [
Rank(
name="C-",
index=0,
group=RANK_GROUPS[0],
rank_up_point=200,
enter_fee=0,
),
Rank(
name="C",
index=1,
group=RANK_GROUPS[0],
rank_up_point=400,
enter_fee=20,
),
Rank(
name="C+",
index=2,
group=RANK_GROUPS[0],
rank_up_point=600,
enter_fee=40,
),
Rank(
name="B-",
index=3,
group=RANK_GROUPS[1],
rank_up_point=350,
enter_fee=55,
),
Rank(
name="B",
index=4,
group=RANK_GROUPS[1],
rank_up_point=600,
enter_fee=70,
),
Rank(
name="B+",
index=5,
group=RANK_GROUPS[1],
rank_up_point=850,
enter_fee=85,
),
Rank(
name="A-",
index=6,
group=RANK_GROUPS[2],
rank_up_point=500,
enter_fee=100,
),
Rank(
name="A",
index=7,
group=RANK_GROUPS[2],
rank_up_point=800,
enter_fee=110,
),
Rank(
name="A+",
index=8,
group=RANK_GROUPS[2],
rank_up_point=1100,
enter_fee=120,
),
Rank(
name="S",
index=9,
group=RANK_GROUPS[3],
rank_up_point=1000,
enter_fee=150,
),
]
for s_group_index in range(5):
for s_rank_index in range(10):
RANKS.append(
Rank(
name=f"S+{s_group_index * 10 + s_rank_index}",
index=10 + (s_group_index * 10 + s_rank_index),
group=RANK_GROUPS[4 + s_group_index],
rank_up_point=650 + (s_rank_index * 350),
enter_fee=160,
)
)
RANKS.append(
Rank(
name=f"S+50",
index=len(RANKS),
group=RANK_GROUPS[9],
rank_up_point=9999,
enter_fee=160,
)
)
class GameLog:
def __init__(self, is_win: bool) -> None:
self.is_win = is_win
def __repr__(self) -> str:
return f"{'Win' if self.is_win else 'Lose'}"
class PlayerRankState:
def __init__(
self,
is_rank_group_up: bool,
) -> None:
self.is_rank_group_up = is_rank_group_up
self.win_count: int = 0
self.lose_count: int = 0
self.logs: list[GameLog] = []
def is_ended(self) -> bool:
if self.is_lose():
return True
if self.is_rank_group_up:
return self.win_count >= 3
return self.win_count >= 5
def is_lose(self) -> bool:
return self.lose_count >= 3
class Team:
def __init__(self) -> None:
self.players: list["Player"] = []
self.players_length: int = 0
self.mean_performance: float = 0.0
self.mean_win_count: float = 0.0
self.min_rank_group_index: int = 9999
def __repr__(self) -> str:
return f"Team(players_length={self.players_length}, mean_performance={self.mean_performance}, mean_win_count={self.mean_win_count}, min_rank_group_index={self.min_rank_group_index})"
def add_player(self, player: "Player") -> None:
self.players.append(player)
self.players_length += 1
if player.rank.index < self.min_rank_group_index:
self.min_rank_group_index = player.rank.index
self.update_mean()
def remove_player(self, player: "Player") -> None:
self.players.remove(player)
self.players_length -= 1
if player.rank.index == self.min_rank_group_index:
self.min_rank_group_index = min([p.rank.index for p in self.players])
self.update_mean()
def pop_player(self) -> "Player":
player = self.players[-1]
self.remove_player(player)
return player
def update_mean(self) -> None:
self.mean_performance = 0.0
self.mean_win_count = 0.0
for player in self.players:
self.mean_performance += player.performance
self.mean_win_count += player.player_rank_state.win_count
self.mean_performance /= self.players_length
self.mean_win_count /= self.players_length
def merge(self, other: "Team") -> None:
self.players.extend(other.players)
self.players_length += other.players_length
if other.min_rank_group_index < self.min_rank_group_index:
self.min_rank_group_index = other.min_rank_group_index
self.update_mean()
def add_win(self) -> None:
for player in self.players:
player.add_win()
if player.is_rank_ended():
player.update_rank()
self.remove_player(player)
player.enter_rank()
else:
if not player.is_want_to_keep_team():
self.remove_player(player)
player.reenter_rank()
def add_lose(self) -> None:
for player in self.players:
player.add_lose()
if player.is_rank_ended():
player.update_rank()
self.remove_player(player)
player.enter_rank()
else:
if not player.is_want_to_keep_team():
self.remove_player(player)
player.reenter_rank()
class Game:
def __init__(self) -> None:
self.team_map: dict[int, dict[int, list[Team]]] = {
team_size: {rank_group_index: [] for rank_group_index in range(len(RANKS))}
for team_size in range(1, 5)
}
self.team_map_size: dict[int, int] = {team_size: 0 for team_size in range(1, 5)}
self.matched_teams: list[list[Team]] = []
def clear_team_map(self) -> None:
self.team_map = {
team_size: {rank_group_index: [] for rank_group_index in range(len(RANKS))}
for team_size in range(1, 5)
}
self.team_map_size = {team_size: 0 for team_size in range(1, 5)}
def enqueue_team(self, team: Team) -> None:
self.team_map[team.players_length][team.min_rank_group_index].append(team)
self.team_map_size[team.players_length] += 1
def is_build_team_completed(self) -> bool:
return (
self.team_map_size[1] == 0
and self.team_map_size[2] == 0
and self.team_map_size[3] == 0
)
def should_break_team(self) -> bool:
return (
self.team_map_size[1] == 0
and 0 <= self.team_map_size[2] <= 1
and 0 <= self.team_map_size[3]
)
def merge_teams(
self,
team_1_size: int,
team_1_rank_group_index: int,
team_1_list_index: int,
team_2_size: int,
team_2_rank_group_index: int,
team_2_list_index: int,
) -> None:
team_1 = self.team_map[team_1_size][team_1_rank_group_index][team_1_list_index]
team_1.merge(
self.team_map[team_2_size][team_2_rank_group_index][team_2_list_index]
)
self.team_map[team_1_size][team_1_rank_group_index][team_1_list_index] = None
self.team_map_size[team_1_size] -= 1
assert self.team_map_size[team_1_size] >= 0
self.team_map[team_2_size][team_2_rank_group_index][team_2_list_index] = None
self.team_map_size[team_2_size] -= 1
assert self.team_map_size[team_2_size] >= 0
self.team_map[team_1.players_length][team_1.min_rank_group_index].append(team_1)
self.team_map_size[team_1.players_length] += 1
def find_index_of_nearest_performance_team(
self,
source_team_size: int,
source_rank_group_index: int,
source_list_index: int,
team_size: int,
rank_group_index: int,
) -> int:
performance = self.team_map[source_team_size][source_rank_group_index][
source_list_index
].mean_performance
if (
source_team_size == team_size
and source_rank_group_index == rank_group_index
):
index = min(
range(len(self.team_map[team_size][rank_group_index])),
key=lambda i: abs(
(
self.team_map[team_size][rank_group_index][i].mean_performance
if (
source_list_index != i
and self.team_map[team_size][rank_group_index][i]
is not None
)
else 9999
)
- performance
),
)
if index == source_list_index:
return -1
else:
index = min(
range(len(self.team_map[team_size][rank_group_index])),
key=lambda i: abs(
(
self.team_map[team_size][rank_group_index][i].mean_performance
if self.team_map[team_size][rank_group_index][i] is not None
else 9999
)
- performance
),
)
if self.team_map[team_size][rank_group_index][index] is None:
return -1
return index
def break_team_map(self) -> None:
for rank_group_index in self.team_map[3]:
for list_index in range(len(self.team_map[3][rank_group_index])):
team = self.team_map[3][rank_group_index][list_index]
player = team.pop_player()
new_team = Team()
new_team.add_player(player)
self.enqueue_team(new_team)
self.team_map[3][rank_group_index][list_index] = None
self.team_map_size[3] -= 1
assert self.team_map_size[3] >= 0
self.team_map[2][rank_group_index].append(team)
self.team_map_size[2] += 1
assert self.team_map_size[2] >= 0
def rebuild_team_map(self) -> None:
for team_size in self.team_map:
count = 0
for rank_group_index in self.team_map[team_size]:
new_list = [
x
for x in self.team_map[team_size][rank_group_index]
if x is not None
]
self.team_map[team_size][rank_group_index] = new_list
count += len(new_list)
self.team_map_size[team_size] = count
def build_team(self) -> None:
count_to_try = 0
while not self.is_build_team_completed() and count_to_try < 100:
count_to_try += 1
if self.should_break_team():
self.break_team_map()
for target_team_size in range(3, 0, -1):
for rank_group_index in self.team_map[target_team_size]:
for target_team_index in range(
len(self.team_map[target_team_size][rank_group_index])
):
if not self.team_map[target_team_size][rank_group_index][
target_team_index
]:
continue
is_merged = False
for team_size_to_merge in range(4 - target_team_size, 0, -1):
if is_merged:
break
if (
len(self.team_map[team_size_to_merge][rank_group_index])
> 0
):
index_to_merge = (
self.find_index_of_nearest_performance_team(
source_team_size=target_team_size,
source_rank_group_index=rank_group_index,
source_list_index=target_team_index,
team_size=team_size_to_merge,
rank_group_index=rank_group_index,
)
)
if index_to_merge != -1:
self.merge_teams(
team_1_size=target_team_size,
team_1_rank_group_index=rank_group_index,
team_1_list_index=target_team_index,
team_2_size=team_size_to_merge,
team_2_rank_group_index=rank_group_index,
team_2_list_index=index_to_merge,
)
is_merged = True
continue
if (
rank_group_index < LAST_RANK_GROUP_INDEX
and len(
self.team_map[team_size_to_merge][
rank_group_index + 1
]
)
> 0
):
index_to_merge = (
self.find_index_of_nearest_performance_team(
source_team_size=target_team_size,
source_rank_group_index=rank_group_index,
source_list_index=target_team_index,
team_size=team_size_to_merge,
rank_group_index=rank_group_index + 1,
)
)
if index_to_merge != -1:
self.merge_teams(
team_1_size=target_team_size,
team_1_rank_group_index=rank_group_index,
team_1_list_index=target_team_index,
team_2_size=team_size_to_merge,
team_2_rank_group_index=rank_group_index + 1,
team_2_list_index=index_to_merge,
)
is_merged = True
continue
self.rebuild_team_map()
if self.is_build_team_completed():
return
while not self.is_build_team_completed():
if self.should_break_team():
self.break_team_map()
for target_team_size in range(3, 0, -1):
for rank_group_index in self.team_map[target_team_size]:
for target_team_index in range(
len(self.team_map[target_team_size][rank_group_index])
):
if not self.team_map[target_team_size][rank_group_index][
target_team_index
]:
continue
is_merged = False
for team_size_to_merge in range(4 - target_team_size, 0, -1):
if is_merged:
break
for rank_group_index_to_merge in self.team_map[
team_size_to_merge
]:
if is_merged:
break
if (
len(
self.team_map[team_size_to_merge][
rank_group_index_to_merge
]
)
> 0
):
index_to_merge = (
self.find_index_of_nearest_performance_team(
source_team_size=target_team_size,
source_rank_group_index=rank_group_index,
source_list_index=target_team_index,
team_size=team_size_to_merge,
rank_group_index=rank_group_index_to_merge,
)
)
if index_to_merge != -1:
self.merge_teams(
team_1_size=target_team_size,
team_1_rank_group_index=rank_group_index,
team_1_list_index=target_team_index,
team_2_size=team_size_to_merge,
team_2_rank_group_index=rank_group_index_to_merge,
team_2_list_index=index_to_merge,
)
is_merged = True
continue
self.rebuild_team_map()
def matchmaking(self) -> None:
self.matched_teams = []
remain_teams: list[Team] = []
for rank_group_index in sorted(self.team_map[4].keys()):
teams = self.team_map[4][rank_group_index]
sorted_teams = sorted(teams, key=lambda team: team.mean_win_count)
while len(sorted_teams) >= 2:
team_1 = sorted_teams.pop()
team_2 = sorted_teams.pop()
self.matched_teams.append([team_1, team_2])
if sorted_teams:
remain_teams.extend(sorted_teams)
while len(remain_teams) >= 2:
team_1 = remain_teams.pop()
team_2 = remain_teams.pop()
self.matched_teams.append([team_1, team_2])
assert len(remain_teams) == 0
def play_games(self) -> None:
self.clear_team_map()
for teams in self.matched_teams:
team_1 = teams[0]
team_2 = teams[1]
performance_diff = team_1.mean_performance - team_2.mean_performance
# team_1_win_prob = ease_in_out_sine((performance_diff + 1) / 2)
# team_1_win_prob = ease_in_out_cubic((performance_diff + 1) / 2)
team_1_win_prob = ease_in_out_quint((performance_diff + 1) / 2)
# is_team_1_win = performance_diff > 0
is_team_1_win = random.uniform(0, 1) < team_1_win_prob
if is_team_1_win:
team_1.add_win()
team_2.add_lose()
else:
team_1.add_lose()
team_2.add_win()
if team_1.players_length > 0:
self.enqueue_team(team_1)
if team_1.players_length > 0:
self.enqueue_team(team_2)
GAME = Game()
class BaseStrategy:
def is_want_to_keep_team(self, player: "Player") -> bool:
raise NotImplementedError
@property
def name(self):
raise NotImplementedError
def __repr__(self) -> str:
return self.name
class Player:
def __init__(
self,
uid: int,
performance: float,
strategy: "BaseStrategy",
) -> None:
self.uid = uid
self.performance = performance
self.strategy = strategy
self.rank: Rank = RANKS[0]
self.point: int = 10
self.total_point: int = 10
self.player_rank_state: PlayerRankState = None
self.enter_rank()
def __repr__(self) -> str:
return f"Player(uid={self.uid}, performance={self.performance}, rank={self.rank}, point={self.point}, total_point={self.total_point}, strategy={self.strategy})"
def is_want_to_keep_team(self) -> bool:
return self.strategy.is_want_to_keep_team(self)
def rank_group_up(self) -> None:
self.rank = RANKS[self.rank.index + 1]
self.point = self.rank.group.init_point
def rank_up(self) -> None:
self.rank = RANKS[self.rank.index + 1]
def should_next_rank_group_up(self) -> bool:
return (
self.point >= self.rank.group.rank_group_up_point
and self.rank.group.index < (len(RANK_GROUPS) - 1)
)
def enter_rank(self) -> None:
self.point -= self.rank.enter_fee
self.total_point -= self.rank.enter_fee
self.player_rank_state = PlayerRankState(
is_rank_group_up=self.should_next_rank_group_up(),
)
new_team = Team()
new_team.add_player(self)
GAME.enqueue_team(new_team)
def reenter_rank(self) -> None:
new_team = Team()
new_team.add_player(self)
GAME.enqueue_team(new_team)
def update_rank(self) -> None:
if self.player_rank_state.is_rank_group_up:
is_win = not self.player_rank_state.is_lose()
if is_win:
self.rank_group_up()
else:
earn_point = self.rank.group.calc_win_point(
self.player_rank_state.win_count
)
self.point += earn_point
self.total_point += earn_point
if (
not self.should_next_rank_group_up()
and self.point > self.rank.rank_up_point
and self.rank.index < (len(RANKS) - 1)
):
self.rank_up()
def add_win(self) -> None:
self.player_rank_state.win_count += 1
self.player_rank_state.logs.append(GameLog(is_win=True))
def add_lose(self) -> None:
self.player_rank_state.lose_count += 1
self.player_rank_state.logs.append(GameLog(is_win=False))
def is_rank_ended(self) -> bool:
return self.player_rank_state.is_ended()
class RandomStrategy(BaseStrategy):
"""랜덤하게 팀을 바꾼다."""
@property
def name(self):
return "Random"
def is_want_to_keep_team(self, player: "Player") -> bool:
return random.random() < 0.5
class TitForTatStrategy(BaseStrategy):
"""이기면 계속 같은 팀, 지면 팀을 바꾼다."""
@property
def name(self):
return "TitForTat"
def is_want_to_keep_team(self, player: "Player") -> bool:
logs = player.player_rank_state.logs
if len(logs) == 0:
return True
return logs[-1].is_win
class TitFor2TatStrategy(BaseStrategy):
"""이기면 계속 같은 팀, 2번 연속 지면 팀을 바꾼다."""
@property
def name(self):
return "TitFor2Tat"
def is_want_to_keep_team(self, player: "Player") -> bool:
logs = player.player_rank_state.logs
if len(logs) < 2:
return True
return logs[-1].is_win or logs[-2].is_win
class AlwaysChangeStrategy(BaseStrategy):
"""항상 팀을 바꾼다."""
@property
def name(self):
return "AlwaysChange"
def is_want_to_keep_team(self, player: "Player") -> bool:
return False
class AlwaysKeepStrategy(BaseStrategy):
"""항상 같은 팀을 유지한다."""
@property
def name(self):
return "AlwaysKeep"
def is_want_to_keep_team(self, player: "Player") -> bool:
return True
class WinStayLoseShiftStrategy(BaseStrategy):
"""팀을 바꾸지 않아서 이기면 계속 팀을 바꾸지 않고, 팀을 바꿔서 이기면 팀을 바꾼다."""
def __init__(self) -> None:
super().__init__()
self.shifter = True
@property
def name(self):
return "WinStayLoseShift"
def is_want_to_keep_team(self, player: "Player") -> bool:
logs = player.player_rank_state.logs
if len(logs) == 0:
return True
if not logs[-1].is_win:
self.shifter = not self.shifter
if self.shifter:
return logs[-1].is_win
return not logs[-1].is_win
if __name__ == "__main__":
NUM_OF_TEAM = 600
NUM_OF_GAMEPLAY = 3000
strategy_classes = [
RandomStrategy,
TitForTatStrategy,
TitFor2TatStrategy,
AlwaysChangeStrategy,
AlwaysKeepStrategy,
WinStayLoseShiftStrategy,
]
players = [
Player(
uid=i,
performance=min(1, max(0, random.gauss(0.5, 0.12))),
strategy=strategy_classes[i % len(strategy_classes)](),
)
for i in range(4 * NUM_OF_TEAM)
]
for _ in tqdm(range(NUM_OF_GAMEPLAY), desc="Gameplay"):
GAME.build_team()
GAME.matchmaking()
GAME.play_games()
for strategy_class in strategy_classes:
strategy_players = [
x for x in players if x.strategy.__class__ == strategy_class
]
name = strategy_class().name
size = len(strategy_players)
mean_performance = sum([x.performance for x in strategy_players]) / size
mean_total_point = sum([x.total_point for x in strategy_players]) / size
mean_rank_index = sum([x.rank.index for x in strategy_players]) / size
print(
f"{name:20} ({size}) - performance: {mean_performance:.8f}, rank: {mean_rank_index:2.8f}({RANKS[round(mean_rank_index)]}), total_point: {mean_total_point:6.8f}"
)
# 실행 결과 예시
# Without Randomness
# Gameplay: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3000/3000 [02:39<00:00, 18.77it/s]
# Random (400) - performance: 0.49155536, rank: 18.42000000(S+8), total_point: 3478.63750000
# TitForTat (400) - performance: 0.49418261, rank: 18.78750000(S+9), total_point: 3701.83750000
# TitFor2Tat (400) - performance: 0.50404856, rank: 18.87500000(S+9), total_point: 3674.96250000
# AlwaysChange (400) - performance: 0.50543801, rank: 19.27500000(S+9), total_point: 4080.11250000
# AlwaysKeep (400) - performance: 0.49695183, rank: 18.50250000(S+9), total_point: 3166.33750000
# WinStayLoseShift (400) - performance: 0.49912069, rank: 18.95750000(S+9), total_point: 3868.17500000
# With ease_in_out_sine
# Gameplay: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3000/3000 [02:54<00:00, 17.19it/s]
# Random (400) - performance: 0.49994154, rank: 20.04000000(S+10), total_point: 5287.25000000
# TitForTat (400) - performance: 0.49548483, rank: 19.85250000(S+10), total_point: 5256.10000000
# TitFor2Tat (400) - performance: 0.51182825, rank: 20.38750000(S+10), total_point: 5410.90000000
# AlwaysChange (400) - performance: 0.49063044, rank: 19.78500000(S+10), total_point: 5245.61250000
# AlwaysKeep (400) - performance: 0.49973944, rank: 19.84500000(S+10), total_point: 5258.98750000
# WinStayLoseShift (400) - performance: 0.49879708, rank: 20.08500000(S+10), total_point: 5288.83750000
# With ease_in_out_cubic
# Gameplay: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3000/3000 [02:57<00:00, 16.93it/s]
# Random (400) - performance: 0.49849383, rank: 20.18750000(S+10), total_point: 5262.45000000
# TitForTat (400) - performance: 0.49335503, rank: 19.92000000(S+10), total_point: 5275.65000000
# TitFor2Tat (400) - performance: 0.49527133, rank: 19.95750000(S+10), total_point: 5255.73750000
# AlwaysChange (400) - performance: 0.49818314, rank: 20.10750000(S+10), total_point: 5332.12500000
# AlwaysKeep (400) - performance: 0.50013418, rank: 20.22500000(S+10), total_point: 5355.62500000
# WinStayLoseShift (400) - performance: 0.50111006, rank: 20.17750000(S+10), total_point: 5319.33750000
# With ease_in_out_quint
# Gameplay: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3000/3000 [02:49<00:00, 17.73it/s]
# Random (400) - performance: 0.50468686, rank: 19.88000000(S+10), total_point: 5190.11250000
# TitForTat (400) - performance: 0.49820626, rank: 19.81250000(S+10), total_point: 5178.57500000
# TitFor2Tat (400) - performance: 0.49942388, rank: 19.83250000(S+10), total_point: 5184.18750000
# AlwaysChange (400) - performance: 0.51059320, rank: 20.27500000(S+10), total_point: 5463.20000000
# AlwaysKeep (400) - performance: 0.50127540, rank: 19.57000000(S+10), total_point: 5074.16250000
# WinStayLoseShift (400) - performance: 0.50879255, rank: 20.16250000(S+10), total_point: 5388.98750000
# 결론
# - 사실상 각 전략별 의미있는 수준의 차이는 없는 것 같다.
# - 이기면 계속 같은 팀, 지면 팀을 바꾸는 전략이 나쁘지 않아 보인다. (심리적으로도...)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment