-
Notifications
You must be signed in to change notification settings - Fork 0
/
romuald.py
163 lines (131 loc) · 6.2 KB
/
romuald.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# This bot looks one move ahead, and if possible it will make a move to
# block its opponent winning. Otherwise, it picks a move at random.
import copy
import random
from botany_connectfour import game
from collections import defaultdict
def cut_fn(x):
return (-x[2],x[3],x[4],x[5])
def remaining_to_win(board,line,token):
line_string = "".join([board[l][r] for l,r in line])
if 'X' in line_string and 'O' in line_string:
return (5,token)
else:
letters = line_string.replace('.','')
letter = token
if len(letters)> 0:
letter = letters[0]
return (line_string.count('.') or 5,letter)
def get_letter_of_pos(position,board):
col, row = position
return board[col][row]
def get_traps(board,the_lines,best,token):
dict_traps = defaultdict(list)
for line in the_lines:
dict_traps[remaining_to_win(board,line,token)[0]].append(line)
if min(dict_traps.keys()) == 1:
return dict_traps[1]
output = list()
for i in [key for key in dict_traps.keys() if key<best]:
output += dict_traps[i]
return output
def get_his_deadly_traps(board,the_lines,best,the_toten):
traps = list()
for line in the_lines:
remaining, letter = remaining_to_win(board,line,the_toten)
if remaining == 1 and letter != the_toten:
traps.append(line)
return traps
def select_for_victory(board,outcome_assign,proposals,token):
for column,_,_,_,_,_ in proposals:
if outcome_assign.get(column) == token:
return column
return column
def get_next_move(board, token):
available_moves = game.available_moves(board)
available_position = [(a,board[a].index('.')) for a in available_moves]
available_play = available_position
if token == 'X':
other_token = 'O'
else:
other_token = 'X'
possible = [line for line in game.LINES_OF_4 if not set(available_position).isdisjoint(line)]
outcome = dict()
best = 5
for line in possible:
remaining, letter = remaining_to_win(board,line,token)
if remaining == 5:
continue
if best > remaining:
outcome = defaultdict(int)
outcome_assign = defaultdict()
best = remaining
if best == remaining:
the_moves = list(set(line) & set(available_play))
for move in the_moves:
outcome[move[0]] += 1
if letter == token:
outcome_assign[move[0]] = letter
#tuple number of line_4, row, cols
proposals = [(i,board[i].index('.'),9,0,0,j) for i,j in outcome.items()]
if best == 1:
return select_for_victory(board,outcome_assign,proposals,token)
traps = get_traps(board,game.LINES_OF_4,best,token)
#Do not fall in a trap.
proposals_minus_traps = [prop for prop in proposals if not any((prop[0],prop[1]+1) in trap for trap in traps)]
friendly_traps = get_his_deadly_traps(board,game.LINES_OF_4,best,other_token)
available_traps = set(pos for trap in friendly_traps for pos in trap if get_letter_of_pos(pos,board) == ".")
enemy_traps = get_his_deadly_traps(board,game.LINES_OF_4,best,token)
for move in available_moves:
sequence_play = sorted([a[1] - board[a[0]].index('.')%2 for a in available_traps if a[0] == move])
if len(sequence_play) > 1:
return move
#We have a choice, let's see if we can set a trap
if len(traps) > 1:
available_enemy_traps = set(pos for trap in enemy_traps for pos in trap if get_letter_of_pos(pos,board) == ".")
board1 = copy.deepcopy(board)
improved_proposals = list()
only_him = set((row,col) for row,col in available_play if not any((row,col+1) in trap for trap in traps)) - set((row,col) for row,col,_,_,_,_ in proposals)
if len(enemy_traps)>0 and len(friendly_traps) == 0:
for col,row in only_him:
board1[col][row] = other_token
new_enemy_traps = get_his_deadly_traps(board1,(enemy_traps+possible),best,token)
all_new_traps = set(pos for trap in new_enemy_traps for pos in trap if get_letter_of_pos(pos,board1) == ".")
new_traps = all_new_traps - available_enemy_traps
if len(new_traps) > 0:
potential_traps = (all_new_traps.union(available_enemy_traps))
sequence_play = [a[1] - board[a[0]].index('.') for a in potential_traps]
if len(potential_traps)>0:
next_one = min(sequence_play)
else:
next_one = 9
order = len(set(a%2 for a in sequence_play))
improved_proposals.append((col,row,next_one,order,len(new_traps),0))
board1[col][row] = '.'
for col,row,_,_,_,remaining in proposals_minus_traps:
board1[col][row] = token
new_friendly_traps = get_his_deadly_traps(board1,(traps+possible),best,other_token)
all_new_traps = set(pos for trap in new_friendly_traps for pos in trap if get_letter_of_pos(pos,board1) == ".")
new_traps = all_new_traps - available_traps
potential_traps = (all_new_traps.union(available_traps))
sequence_play = [a[1] - board[a[0]].index('.') for a in potential_traps]
if len(potential_traps)>0:
next_one = min(sequence_play)
else:
next_one = 9
order = len(set(a%2 for a in sequence_play))
improved_proposals.append((col,row,next_one,order,len(new_traps),remaining))
board1[col][row] = '.'
if len(improved_proposals):
proposals_minus_traps = improved_proposals
outputs = list()
if len(proposals_minus_traps):
outputs = [e[0] for e in proposals_minus_traps if e == max(proposals_minus_traps,key=cut_fn)]
if len(outputs) == 0:
outputs = available_moves
outputs = [move for move in outputs if not any((move,board[move].index('.')+1) in trap for trap in enemy_traps)]
try:
output = random.choice(outputs)
except:
output = random.choice(available_moves)
return output