does not work, but is improved
This commit is contained in:
parent
56ceee589b
commit
bd87ed86d0
176
board.py
176
board.py
|
@ -1,24 +1,19 @@
|
||||||
from cup import Cup
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import itertools
|
||||||
|
|
||||||
class Board:
|
class Board:
|
||||||
|
|
||||||
|
initial_state = [ 0,
|
||||||
|
2, 0, 0, 0, 0, -5,
|
||||||
|
0, -3, 0, 0, 0, 5,
|
||||||
|
-5, 0, 0, 0, 3, 0,
|
||||||
|
5, 0, 0, 0, 0, -2,
|
||||||
|
0 ]
|
||||||
|
|
||||||
# TODO: Remember to handle pushing other pieces to home
|
# TODO: Remember to handle pushing other pieces to home
|
||||||
# TODO: Also remember that a player can't move backwards and the one
|
# TODO: Also remember that a player can't move backwards and the one
|
||||||
# player goes from 1-47 while the other goes from 47-1
|
# player goes from 1-47 while the other goes from 47-1
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.state = [ 0,
|
|
||||||
2, 0, 0, 0, 0, -5,
|
|
||||||
0, -3, 0, 0, 0, 5,
|
|
||||||
-5, 0, 0, 0, 3, 0,
|
|
||||||
5, 0, 0, 0, 0, -2,
|
|
||||||
0, 0, 0 ]
|
|
||||||
|
|
||||||
|
|
||||||
def get_state(self):
|
|
||||||
return self.state
|
|
||||||
|
|
||||||
# Remember to handle edge case when we're on the last moves and you may go
|
# Remember to handle edge case when we're on the last moves and you may go
|
||||||
# from position 22 -> 24 on a 6, if you have no pieces behind 22. Simply
|
# from position 22 -> 24 on a 6, if you have no pieces behind 22. Simply
|
||||||
# check if any are behind if you're circle or if any are higher if you are
|
# check if any are behind if you're circle or if any are higher if you are
|
||||||
|
@ -35,64 +30,97 @@ class Board:
|
||||||
# TODO: handle barring in a more elengant way
|
# TODO: handle barring in a more elengant way
|
||||||
# TODO: allow bearing off with non-exact die roll if this is only possible
|
# TODO: allow bearing off with non-exact die roll if this is only possible
|
||||||
# move
|
# move
|
||||||
def check_move(self, move, player, roll):
|
# TODO: Allow not doing anything if and only if no alternatives
|
||||||
from_idx = int(move[0])
|
@staticmethod
|
||||||
to_idx = int(move[1])
|
def is_move_valid(board, player, face_value, move):
|
||||||
to_state = self.state[to_idx]
|
board = list(board)
|
||||||
from_state = self.state[from_idx]
|
from_idx = move[0]
|
||||||
|
to_idx = move[1]
|
||||||
|
to_state = board[to_idx]
|
||||||
|
from_state = board[from_idx]
|
||||||
|
|
||||||
if from_idx == 26:
|
if from_idx == 26:
|
||||||
from_idx = 1
|
from_idx = 1
|
||||||
roll -= 1
|
face_value -= 1
|
||||||
elif from_idx == 27:
|
elif from_idx == 27:
|
||||||
from_idx = 24
|
from_idx = 24
|
||||||
roll -= 1
|
face_value -= 1
|
||||||
|
|
||||||
if not (1 <= from_idx <= 24 and
|
if not (1 <= from_idx <= 24 and
|
||||||
1 <= to_idx <= 24):
|
1 <= to_idx <= 24):
|
||||||
return False
|
return False
|
||||||
elif ( abs( from_idx - to_idx ) != roll ):
|
elif ( abs( from_idx - to_idx ) != face_value ):
|
||||||
return False
|
return False
|
||||||
elif (from_state * player >= 1 and # Is moving player's own checker?
|
elif (from_state * player >= 1 and # Is moving player's own checker?
|
||||||
(to_state * player >= 0 or # Is 'to' empty or has player's own checkers?
|
(to_state * player >= 0 or # Is 'to' empty or has player's own checkers?
|
||||||
to_state * -player == 1)): # Can opponent checker be hit?
|
to_state * -player == 1)): # Can opponent checker be hit?
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def find_pieces_for_player(self, player):
|
@staticmethod
|
||||||
idxs = []
|
def is_winner(board, player):
|
||||||
for idx, pip in enumerate(self.state):
|
|
||||||
if pip * player >= 1:
|
|
||||||
idxs.append(idx)
|
|
||||||
return idxs
|
|
||||||
|
|
||||||
def is_winner(self, player):
|
|
||||||
for i in range(1,25):
|
for i in range(1,25):
|
||||||
if player * self.state[i] > 0:
|
if player * board[i] > 0:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def find_legal_moves(self, player, roll):
|
def calculate_legal_states(board, player, roll):
|
||||||
# Find all pips with things on them belonging to the player
|
# Find all pips with things on them belonging to the player
|
||||||
# Iterate through each index and check if it's a possible move given the roll
|
# Iterate through each index and check if it's a possible move given the roll
|
||||||
# If player is O, then check for idx + roll
|
# If player is O, then check for idx + roll
|
||||||
# If player is X, then check for idx - roll
|
# If player is X, then check for idx - roll
|
||||||
|
print("Find legal moves: ",roll,"-"*20)
|
||||||
# Rewrite this, it's shit.
|
|
||||||
idxs_with_thing = self.find_pieces_for_player(player)
|
|
||||||
legal_moves = []
|
|
||||||
|
|
||||||
for index in idxs_with_thing:
|
def idxs_with_checkers_of_current_player(board):
|
||||||
from_idx = index
|
idxs = []
|
||||||
to_idx = index+(roll*player)
|
for idx, checker_count in enumerate(board):
|
||||||
if self.check_move([from_idx,to_idx], player, roll):
|
if checker_count * player >= 1:
|
||||||
legal_moves.append([from_idx,to_idx])
|
idxs.append(idx)
|
||||||
|
return idxs
|
||||||
|
|
||||||
|
def calc_moves(board, face_value):
|
||||||
|
idxs_with_checkers = idxs_with_checkers_of_current_player(board)
|
||||||
|
boards = [(do_move(board,
|
||||||
|
player,
|
||||||
|
(idx, idx + (face_value * player))) if
|
||||||
|
is_move_valid(board,
|
||||||
|
player,
|
||||||
|
face_value,
|
||||||
|
(idx, idx + (face_value * player))) else None)
|
||||||
|
for idx
|
||||||
|
in idxs_with_checkers]
|
||||||
|
|
||||||
|
# If no move can be made, make sure to include current board
|
||||||
|
if is_move_valid(board, player, face_value, None):
|
||||||
|
boards.append(board)
|
||||||
|
|
||||||
|
return list(filter(None, boards)) # Remove None-values
|
||||||
|
|
||||||
|
# ------------------
|
||||||
|
# 1. Determine if dice have identical face value
|
||||||
|
# 2. Iterate through remaining dice
|
||||||
|
|
||||||
|
legal_moves = set()
|
||||||
|
|
||||||
|
dice_permutations = list(itertools.permutations(roll)) if roll[0] != roll[1] else [[roll[0]]*4]
|
||||||
|
|
||||||
|
for pair in dice_permutations:
|
||||||
|
# Calculate boards resulting from first move
|
||||||
|
boards = calc_moves(board, pair[0])
|
||||||
|
|
||||||
|
for x in dice_permutations[1:None]:
|
||||||
|
# Calculate boards resulting from second move
|
||||||
|
nested_boards = [calc_moves(board, x) for board in boards]
|
||||||
|
boards = [board for boards in nested_boards for board in boards]
|
||||||
|
# Add resulting unique boards to set of legal boards resulting from roll
|
||||||
|
legal_moves = legal_moves | set(boards)
|
||||||
|
|
||||||
return legal_moves
|
return legal_moves
|
||||||
|
|
||||||
def pretty(self):
|
@staticmethod
|
||||||
|
def pretty(board):
|
||||||
temp = []
|
temp = []
|
||||||
for x in self.state:
|
for x in board:
|
||||||
if x >= 0:
|
if x >= 0:
|
||||||
temp.append(" {}".format(x))
|
temp.append(" {}".format(x))
|
||||||
else:
|
else:
|
||||||
|
@ -101,28 +129,46 @@ class Board:
|
||||||
return """
|
return """
|
||||||
13 14 15 16 17 18 19 20 21 22 23 24
|
13 14 15 16 17 18 19 20 21 22 23 24
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
| {12}| {11}| {10}| {9}| {8}| {7}| bar 1: {26} | {6}| {5}| {4}| {3}| {2}| {1}| end -1: {0}|
|
| {11}| {10}| {9}| {8}| {7}| {6}| bar -1: {24} | {5}| {4}| {3}| {2}| {1}| {0}| end -1: TODO|
|
||||||
|---|---|---|---|---|---|-----------|---|---|---|---|---|---|
|
|---|---|---|---|---|---|-----------|---|---|---|---|---|---|
|
||||||
| {13}| {14}| {15}| {16}| {17}| {18}| bar -1: {27} | {19}| {20}| {21}| {22}| {23}| {24}| end 1: {25}|
|
| {12}| {13}| {14}| {15}| {16}| {17}| bar 1: {25} | {18}| {19}| {20}| {21}| {22}| {23}| end 1: TODO|
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
12 11 10 9 8 7 6 5 4 3 2 1
|
12 11 10 9 8 7 6 5 4 3 2 1
|
||||||
""".format(*temp)
|
""".format(*temp)
|
||||||
def move_to_bar(self, to_idx):
|
|
||||||
# Find the owner of the hit checker
|
|
||||||
player = self.state[to_idx]
|
|
||||||
|
|
||||||
# FIXME: find better bar solution
|
@staticmethod
|
||||||
if player == 1:
|
def do_move(board, player, move):
|
||||||
self.state[26] += player
|
# Implies that move is valid; make sure to check move validity before calling do_move(...)
|
||||||
else:
|
|
||||||
self.state[27] += player
|
def move_to_bar(board, to_idx):
|
||||||
|
board = list(board)
|
||||||
self.state[to_idx] = 0
|
if player == 1:
|
||||||
|
board[0] += player
|
||||||
def move_thing(self, player, from_idx, to_idx):
|
else:
|
||||||
self.state[from_idx] -= player
|
board[25] += player
|
||||||
|
|
||||||
if self.state[to_idx] * player == -1:
|
|
||||||
self.move_to_bar(to_idx)
|
|
||||||
|
|
||||||
self.state[to_idx] += player
|
board[to_idx] = 0
|
||||||
|
return board
|
||||||
|
|
||||||
|
# TODO: Moving in from bar is handled by the representation
|
||||||
|
# TODONE: Handle bearing off
|
||||||
|
|
||||||
|
from_idx = move[0]
|
||||||
|
to_idx = move[1]
|
||||||
|
board = list(board) # Clone board
|
||||||
|
|
||||||
|
# 'Lift' checker
|
||||||
|
board[from_idx] -= player
|
||||||
|
|
||||||
|
# Handle bearing off
|
||||||
|
if to_idx < 1 or to_idx > 24:
|
||||||
|
return board
|
||||||
|
|
||||||
|
# Handle hitting checkers
|
||||||
|
if board[to_idx] * player == -1:
|
||||||
|
board = move_to_bar(board, to_idx)
|
||||||
|
|
||||||
|
# Put down checker
|
||||||
|
board[to_idx] += player
|
||||||
|
|
||||||
|
return board
|
||||||
|
|
23
bot.py
23
bot.py
|
@ -1,13 +1,12 @@
|
||||||
from cup import Cup
|
from cup import Cup
|
||||||
|
from board import Board
|
||||||
import random
|
import random
|
||||||
|
|
||||||
class Bot:
|
class Bot:
|
||||||
|
|
||||||
def __init__(self, board, sym, network):
|
def __init__(self, sym):
|
||||||
self.cup = Cup()
|
self.cup = Cup()
|
||||||
self.board = board
|
|
||||||
self.sym = sym
|
self.sym = sym
|
||||||
self.network = network
|
|
||||||
|
|
||||||
def roll(self):
|
def roll(self):
|
||||||
print("{} rolled: ".format(self.sym))
|
print("{} rolled: ".format(self.sym))
|
||||||
|
@ -22,20 +21,20 @@ class Bot:
|
||||||
def get_sym(self):
|
def get_sym(self):
|
||||||
return self.sym
|
return self.sym
|
||||||
|
|
||||||
def do_move(self, roll):
|
def do_move(board, roll):
|
||||||
print(self.board.pretty())
|
print(Board.pretty(board))
|
||||||
print(self.board.find_legal_moves(self.sym, roll[0]))
|
print(Board.find_legal_moves(board, self.sym, roll[0]))
|
||||||
moves_1 = self.board.find_legal_moves(self.sym,roll[0])
|
moves_1 = Board.find_legal_moves(board, self.sym,roll[0])
|
||||||
move = random.choice(moves_1)
|
move = random.choice(moves_1)
|
||||||
print("{} was picked as move".format(move))
|
print("{} was picked as move".format(move))
|
||||||
self.board.move_thing(self.sym, int(move[0]), int(move[1]))
|
new_board = Board.move_thing(self.sym, int(move[0]), int(move[1]))
|
||||||
|
|
||||||
print(self.board.pretty())
|
print(Board.pretty(new_board))
|
||||||
print(self.board.find_legal_moves(self.sym, roll[1]))
|
print(Board.find_legal_moves(new_board, self.sym, roll[1]))
|
||||||
moves_2 = self.board.find_legal_moves(self.sym,roll[1])
|
moves_2 = Board.find_legal_moves(new_board, self.sym,roll[1])
|
||||||
move = random.choice(moves_2)
|
move = random.choice(moves_2)
|
||||||
print("{} was picked as move".format(move))
|
print("{} was picked as move".format(move))
|
||||||
self.board.move_thing(self.sym, int(move[0]), int(move[1]))
|
return Board.move_thing(new_board, self.sym, int(move[0]), int(move[1]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
15
game.py
15
game.py
|
@ -6,10 +6,9 @@ from cup import Cup
|
||||||
|
|
||||||
class Game:
|
class Game:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.board = Board()
|
self.board = Board.initial_state
|
||||||
self.network = Network()
|
self.p1 = Human(1)
|
||||||
self.p1 = Human(self.board, 1, self.network)
|
self.p2 = Bot(-1)
|
||||||
self.p2 = Bot(self.board, -1, self.network)
|
|
||||||
self.cup = Cup()
|
self.cup = Cup()
|
||||||
|
|
||||||
def roll(self):
|
def roll(self):
|
||||||
|
@ -19,15 +18,15 @@ class Game:
|
||||||
while True:
|
while True:
|
||||||
roll = self.roll()
|
roll = self.roll()
|
||||||
print("{} rolled: {}".format(self.p1.get_sym(), roll))
|
print("{} rolled: {}".format(self.p1.get_sym(), roll))
|
||||||
self.p1.do_move(roll)
|
self.board = self.p1.do_move(self.board, roll)
|
||||||
if self.board.is_winner(self.p1.get_sym()):
|
if Board.is_winner(board, self.p1.get_sym()):
|
||||||
print("{} won!".format(self.p1.get_sym()))
|
print("{} won!".format(self.p1.get_sym()))
|
||||||
break
|
break
|
||||||
|
|
||||||
roll = self.roll()
|
roll = self.roll()
|
||||||
print("{} rolled: {}".format(self.p1.get_sym(), roll))
|
print("{} rolled: {}".format(self.p1.get_sym(), roll))
|
||||||
self.p2.do_move(roll)
|
self.board = self.p2.do_move(self.board, roll)
|
||||||
if self.board.is_winner(self.p2.get_sym()):
|
if Board.is_winner(board, self.p2.get_sym()):
|
||||||
print("{} won!".format(self.p2.get_sym()))
|
print("{} won!".format(self.p2.get_sym()))
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
28
human.py
28
human.py
|
@ -1,7 +1,7 @@
|
||||||
|
from board import Board
|
||||||
|
|
||||||
class Human:
|
class Human:
|
||||||
def __init__(self, board, sym, network):
|
def __init__(self, sym):
|
||||||
self.network = network
|
|
||||||
self.board = board
|
|
||||||
self.sym = sym
|
self.sym = sym
|
||||||
|
|
||||||
def switch(self,cur):
|
def switch(self,cur):
|
||||||
|
@ -10,30 +10,30 @@ class Human:
|
||||||
def get_sym(self):
|
def get_sym(self):
|
||||||
return self.sym
|
return self.sym
|
||||||
|
|
||||||
def do_move(self, roll):
|
def do_move(self, board, roll):
|
||||||
print(self.board.pretty())
|
print(Board.pretty(board))
|
||||||
print(self.board.find_legal_moves(self.sym,roll[0]))
|
print("Human: ",roll,"-"*20)
|
||||||
cur_state = self.board.get_state()
|
print(Board.find_legal_moves(board,self.sym,roll[0]))
|
||||||
|
|
||||||
print("What to do with the first roll?")
|
print("What to do with the first roll?")
|
||||||
cmds_1 = input().split(",")
|
cmds_1 = input().split(",")
|
||||||
|
|
||||||
while not self.board.check_move(cmds_1, self.sym, roll[0]):
|
while not Board.is_move_valid(board, roll[0], self.sym, cmds_1):
|
||||||
print("Invalid move, try again.")
|
print("Invalid move, try again.")
|
||||||
cmds_1 = input().split(",")
|
cmds_1 = input().split(",")
|
||||||
|
|
||||||
self.board.move_thing(self.sym, int(cmds_1[0]), int(cmds_1[1]))
|
new_board = Board.move_thing(board, self.sym, int(cmds_1[0]), int(cmds_1[1]))
|
||||||
|
|
||||||
|
|
||||||
print(self.board.pretty())
|
print(Board.pretty(board))
|
||||||
print(self.board.find_legal_moves(self.sym,roll[1]))
|
print(Board.find_legal_moves(new_board, self.sym,roll[1]))
|
||||||
print("What to do with the second roll?")
|
print("What to do with the second roll?")
|
||||||
cmds_2 = input().split(",")
|
cmds_2 = input().split(",")
|
||||||
while not self.board.check_move(cmds_2, self.sym, roll[1]):
|
while not Board.is_move_valid(new_board, roll[1], self.sym, cmds_2):
|
||||||
print("Invalid move")
|
print("Invalid move")
|
||||||
cmds_2 = input().split(",")
|
cmds_2 = input().split(",")
|
||||||
|
|
||||||
|
|
||||||
self.board.move_thing(self.sym, int(cmds_2[0]), int(cmds_2[1]))
|
return Board.move_thing(new_board, self.sym, int(cmds_2[0]), int(cmds_2[1]))
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user