class Human(Player):
def select_move(self):
moves = self.game.valid_moves()
choice = ""
while not choice.isnumeric() or len(moves) < int(choice):
print("Your choice: ", end="")
choice = input()
return moves[int(choice) - 1]
class Nim(Game):
def __init__(self, player1, player2, counts):
super().__init__(player1, player2)
self.counts = counts
def has_ended(self):
for index in range(0, len(self.counts)):
if self.counts[index] > 0:
return False
return True
def winner(self):
return self.current_player
def make_move(self, move):
heap = move[0]
count = move[1]
self.counts[heap] = self.counts[heap] - count
def valid_moves(self):
moves = []
for heap in range(0, len(self.counts)):
for count in range(1, self.counts[heap] + 1):
moves.append([heap, count])
return moves
def __str__(self):
lines = []
lines.append(super().__str__())
min = 0
for index in range(0, len(self.counts)):
count = self.counts[index]
options = []
for number in range(min + 1, min + count + 1):
options.append(str(number))
lines.append(" ".join(options))
min = min + count
return "\n".join(lines)
class TicTacToe(Game):
def __init__(self, player1, player2):
super().__init__(player1, player2)
self.grid = [" "] * 9
def has_ended(self):
return not (" " in self.grid) or self.winner() != None
def lines(self):
result = []
for row in range(0, 3):
result.append(self.grid[3 * row : 3 * (row + 1)])
for col in range(0, 3):
result.append(
self.grid[col : col + 1]
+ self.grid[col + 3 : col + 4]
+ self.grid[col + 6 : col + 7]
)
result.append(self.grid[0:1] + self.grid[4:5] + self.grid[8:9])
result.append(self.grid[2:3] + self.grid[4:5] + self.grid[6:7])
return result
def winner(self):
current_char = str(self.current_player)[0]
waiting_char = str(self.waiting_player)[0]
if ([current_char] * 3) in self.lines():
return self.current_player
if ([waiting_char] * 3) in self.lines():
return self.waiting_player
return None
def make_move(self, move):
self.grid[move] = str(self.current_player)[0]
def undo_move(self, move):
self.grid[move] = " "
def valid_moves(self):
moves = []
for index in range(0, len(self.grid)):
if self.grid[index] == " ":
moves.append(index)
return moves
def __str__(self):
grid = self.grid + []
moves = self.valid_moves()
for index in range(0, len(moves)):
grid[moves[index]] = str(index + 1)
result = super().__str__() + "\n"
for row in range(0, 3):
for col in range(0, 3):
result = result + " " + grid[3 * row + col]
result = result + "\n"
return result
Da bei Reversi eine zufällige Bewertungsfunktion in Kombination mit Spielbaumsuche erstaunlich gute Ergebnisse liefert, ist es gar nicht so leicht, eine bessere anzugeben. Die folgende Implementierung basiert auf den in der Aufgabenstellung genannten Kriterien.
def relative_eval(one, two):
total = one + two
if total == 0:
return 0.5
return one / total
class ReversiPlayer(PruningPlayer):
def count_discs_at(self, player, indices):
tiles = []
for index in range(0, len(indices)):
tiles.append(self.game.all_tiles[indices[index]])
return self.game.count_discs(player, tiles)
def eval_on_stop(self):
if self.game.has_ended():
return self.game.eval_on_end()
current_discs = self.game.count_discs(
self.game.current_player, self.game.all_tiles
)
waiting_discs = self.game.count_discs(
self.game.waiting_player, self.game.all_tiles
)
disc_eval = relative_eval(current_discs, waiting_discs)
current_moves = len(self.game.valid_moves())
self.game.next_turn()
waiting_moves = len(self.game.valid_moves())
self.game.next_turn()
move_eval = relative_eval(current_moves, waiting_moves)
progress = (current_discs + waiting_discs) / 64
disc_move_eval = progress * disc_eval + (1 - progress) * move_eval
corners = [0, 7, 56, 63]
current_corners = self.count_discs_at(self.game.current_player, corners)
waiting_corners = self.count_discs_at(self.game.waiting_player, corners)
corner_eval = relative_eval(current_corners, waiting_corners)
return (disc_move_eval + corner_eval) / 2