-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathothello.py
153 lines (143 loc) · 5.92 KB
/
othello.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
import numpy as np
from constants import EMPTY, WHITE, BLACK, VALID_MOVE, DIRECTIONS, BLACK_WON, WHITE_WON, DRAW, GAME_IN_PROGRESS,\
MAXIMIZING_PLAYER
from move import Move
class Othello:
def __init__(self, n):
self.n = n
self.state = np.zeros((self.n, self.n), dtype=np.uint8)
self.state[self.n // 2, self.n // 2] = WHITE
self.state[self.n // 2 - 1, self.n // 2 - 1] = WHITE
self.state[self.n // 2 - 1, self.n // 2] = BLACK
self.state[self.n // 2, self.n // 2 - 1] = BLACK
self.black_score = 2
self.white_score = 2
self.no_moves_semaphore = 0
self.last_move = None
def move_generator(self, player):
moves = []
self.state[self.state == VALID_MOVE] = EMPTY
pieces_indices = np.argwhere(self.state == player)
opponent = WHITE if player == BLACK else BLACK
for index in pieces_indices:
for direction in DIRECTIONS:
self.single_piece_move_generator(player, opponent, index, direction, moves)
if len(moves) == 0:
self.no_moves_semaphore += 1
else:
self.no_moves_semaphore = 0
return moves
def single_piece_move_generator(self, player, opponent, start, direction, moves):
delta_x = 0
delta_y = 0
if direction in ("north", "north_west", "north_east"):
delta_x = -1
elif direction in ("south", "south_west", "south_east"):
delta_x = 1
if direction in ("west", "north_west", "south_west"):
delta_y = -1
elif direction in ("east", "north_east", "south_east"):
delta_y = 1
x = start[0] + delta_x
y = start[1] + delta_y
last_seen_opponent_piece = None
while not self.out_of_bounds(x, y):
if self.state[x, y] == opponent:
last_seen_opponent_piece = [x, y]
elif self.state[x, y] == player:
break
elif self.state[x, y] == EMPTY:
if last_seen_opponent_piece is not None:
if not self.out_of_bounds(x, y):
self.state[x, y] = VALID_MOVE
moves.append(Move(x, y))
break
break
elif self.state[x, y] == VALID_MOVE:
break
x += delta_x
y += delta_y
def out_of_bounds(self, x, y):
return x < 0 or y < 0 or x >= self.n or y >= self.n
def apply_move(self, player, move):
self.state[self.state == VALID_MOVE] = EMPTY
self.state[move[0], move[1]] = player
self.last_move = Move(move[0], move[1])
if player == BLACK:
self.black_score += 1
else:
self.white_score += 1
opponent = WHITE if player == BLACK else BLACK
for direction in DIRECTIONS:
self.apply_move_single_direction(player, opponent, move, direction)
def apply_move_single_direction(self, player, opponent, move, direction):
delta_x = 0
delta_y = 0
if direction in ("north", "north_west", "north_east"):
delta_x = -1
elif direction in ("south", "south_west", "south_east"):
delta_x = 1
if direction in ("west", "north_west", "south_west"):
delta_y = -1
elif direction in ("east", "north_east", "south_east"):
delta_y = 1
x = move[0] + delta_x
y = move[1] + delta_y
pieces_to_be_flipped = []
while not self.out_of_bounds(x, y):
if self.state[x, y] == opponent:
pieces_to_be_flipped.append([x, y])
elif self.state[x, y] == player:
if player == BLACK:
self.black_score += len(pieces_to_be_flipped)
self.white_score -= len(pieces_to_be_flipped)
else:
self.white_score += len(pieces_to_be_flipped)
self.black_score -= len(pieces_to_be_flipped)
for piece in pieces_to_be_flipped:
self.state[piece[0], piece[1]] = player
break
elif self.state[x, y] == EMPTY:
break
x += delta_x
y += delta_y
def status(self):
if self.black_score == 0:
self.white_score = self.n * self.n
return WHITE_WON
elif self.white_score == 0:
self.black_score = self.n * self.n
return BLACK_WON
if self.black_score + self.white_score == self.n * self.n or self.no_moves_semaphore >= 2:
if self.black_score > self.white_score:
self.black_score = self.n * self.n - self.white_score
return BLACK_WON
if self.white_score > self.black_score:
self.white_score = self.n * self.n - self.black_score
return WHITE_WON
if self.black_score == self.white_score:
return DRAW
return GAME_IN_PROGRESS
def simple_evaluation_fn(self):
game_status = self.status()
if game_status == GAME_IN_PROGRESS or game_status == DRAW:
if MAXIMIZING_PLAYER == WHITE:
self.last_move.value = self.white_score - self.black_score
else:
self.last_move.value = self.black_score - self.white_score
elif game_status == BLACK_WON:
if MAXIMIZING_PLAYER == BLACK:
self.last_move.value = 100
else:
self.last_move.value = -100
elif game_status == WHITE_WON:
if MAXIMIZING_PLAYER == WHITE:
self.last_move.value = 100
else:
self.last_move.value = -100
def advanced_evaluation_fn(self, player):
moves = self.move_generator(player)
number_of_moves = len(moves)
if player == MAXIMIZING_PLAYER:
self.last_move.value = number_of_moves
self.last_move.value = -number_of_moves