Hive/player - Copy.txt
2024-01-12 22:06:42 +01:00

407 lines
13 KiB
Text

import base as Base
import copy
import random
import time
import math
from PIL import Image, ImageDraw
from collections import deque
# Player template for HIVE --- ALP semestral work
# Vojta Vonasek, 2023
# PUT ALL YOUR IMPLEMENTATION INTO THIS FILE
def inBoard(p, q, size):
""" return True if (p,q) is valid coordinate """
return (q >= 0) and (q < size) and (p >= -(q//2)) and (p < (size - q//2))
def get_neighbors(p, q):
directions = [(0, -1), (1, -1), (1, 0), (0, 1), (-1, 1), (-1, 0)]
return [(p + dp, q + dq) for dp, dq in directions]
def has_neighbors(p, q, board, pp, pq):
# Check if the position (p, q) has at least one neighbor
board = copy.deepcopy(board)
board[pp][pq] = ""
for dp, dq in get_neighbors(0, 0):
neighbor_p, neighbor_q = p + dp, q + dq
if inBoard(neighbor_p, neighbor_q, 13) and board[neighbor_p][neighbor_q] != "":
return True
return False
def connection_crawler(spots, s, visited):
n = 0
for i in spots[s][0]:
if i not in visited and spots[i][1]:
visited.add(i)
n += 1
n += connection_crawler(spots, i, visited)
return n
class Piece:
def __init__(self, p, q, team):
self.p = p
self.q = q
self.team = team
def get_piece_info(self, myisupper):
class_to_letter = {
"Bee": "Q",
"Beetle": "B",
"Spider": "S",
"Grasshopper": "G",
"Ant": "A",
}
letter = class_to_letter[self.__class__.__name__]
if not myisupper:
letter = letter.lower()
return [letter, self.p, self.q]
def get_valid_jumps() -> list:
raise NotImplementedError
class Bee(Piece):
def get_valid_jumps(self, spots, s) -> list:
result = []
for i in spots[s][0]:
if spots[i][1] is None:
hive = False
empty = False
for j in spots[i][0]:
if hive and empty:
break
if j in spots[s][0]:
if spots[j][1] is None:
empty = True
else:
hive = True
if (hive and empty):
result.append(i)
return result
class Beetle(Piece):
def __init__(self, p, q, team, stack=""):
super().__init__(p, q, team)
self.stack = stack
def get_valid_jumps(self, spots, s):
if len(spots[s][1].stack) > 0:
return spots[s][0]
result = []
for i in spots[s][0]:
cs = copy.deepcopy(spots)
cs[s][1] = None
for j in spots[i][0]:
if j in spots.keys() and cs[j][1]:
result.append(i)
break
return result
class Spider(Piece):
def get_valid_jumps(self, spots: dict, s: tuple):
return self.crawl(spots, s, 0, set())
def crawl(self, spots, s, n, visited):
n += 1
visited.add(s)
if n == 4:
return [s]
result = []
for i in spots[s][0]:
hive = False
empty = False
if i not in visited and spots[i][1] is None:
for j in spots[i][0]:
if empty and hive:
break
if j in spots[s][0]:
if spots[j][1] is None or j in visited:
empty = True
else:
hive = True
if (empty and hive):
result += self.crawl(spots, i, n, visited.copy())
return result
class Grasshopper(Piece):
def get_valid_jumps(self, spots, s):
result = []
for i in get_neighbors(0, 0):
q = (s[0]+i[0], s[1]+i[1])
if q in spots.keys() and spots[q][1]:
while True:
q = (q[0]+i[0], q[1]+i[1])
if q in spots.keys() and spots[q][1] is None:
nei = [1 for n in spots[q][0] if n!=s and spots[n][1]]
if sum(nei)>0:
result.append(q)
break
if q not in spots.keys():
break
return result
class Ant(Piece):
def get_valid_jumps(self, spots: dict, s: tuple):
return self.crawl(spots, s, set())
def crawl(self, spots: dict, s: tuple, visited: set):
visited.add(s)
result = []
for i in spots[s][0]:
hive = False
empty = False
if i not in visited and spots[i][1] is None:
for j in spots[i][0]:
if empty and hive:
break
if j in spots[s][0]:
if spots[j][1] is None or j in visited:
empty = True
else:
hive = True
if empty and hive:
result += [i]
result += self.crawl(spots, i, visited)
return result
class Player(Base.Board):
def __init__(
self, playerName, myIsUpper, size, myPieces, rivalPieces
): # do not change this line
Base.Board.__init__(
self, myIsUpper, size, myPieces, rivalPieces
) # do not change this line
self.playerName = playerName
self.algorithmName = "just roll the dice, eh?"
self.spots = dict()
self.figures_player = list()
self.bee = None
self.total_piece_count = 18
self.all_placed = list()
@property
def all_remaining(self):
return self.total_piece_count - sum(list(self.myPieces.values())+list(self.rivalPieces.values()))
@property
def remaining_pieces(self):
return sum(self.myPieces.values())
def connections_checker(self, spots, s):
if isinstance(spots[s][1], Beetle):
if len(spots[s][1].stack) > 0:
return True
for i in spots[s][0]:
if spots[i][1] is None:
continue
visited = set()
visited.add(s)
visited.add(i)
return len(self.all_placed)-2 == connection_crawler(spots, i, visited)
return False
def getAllEmptyCells(self):
result = []
for p in self.board:
for q in self.board[p]:
if self.isEmpty(p, q, self.board):
result.append([p, q])
return result
def getAllNonemptyCells(self):
result = []
for p in self.board:
for q in self.board[p]:
if not self.isEmpty(p, q, self.board):
result.append([p, q])
return result
def translate_board(self):
# mapper dict
piece_class_mapping = {
"Q": Bee,
"B": Beetle,
"S": Spider,
"G": Grasshopper,
"A": Ant,
}
spots = dict()
self.all_placed = list()
figures_player = list()
for p, r in self.board.items():
for q, a in r.items():
surr = [s for s in get_neighbors(
p, q) if self.inBoard(s[0], s[1])]
if a:
if len(a) > 1 and a[-1].lower == "b":
spots[(p,q)] = [surr, piece_class_mapping["B"](p, q, self.myColorIsUpper == a[-1].isupper(), a[:-1])]
else:
spots[(p, q)] = [surr, piece_class_mapping[a[-1].upper()](
p, q, self.myColorIsUpper == a[-1].isupper())]
else:
spots[(p,q)] = [surr, None]
if a and self.myColorIsUpper == a[-1].isupper():
figures_player.append((p, q))
if "Q" in list(map(lambda x: x.upper(),a)):
self.bee = Bee(p, q, True)
if a != "":
self.all_placed.append(piece_class_mapping[a[-1].upper()](
p, q, self.myColorIsUpper == a[-1].isupper()))
self.spots = spots
self.figures_player = figures_player
def get_movable_and_empties(self):
moveable = {}
empty_spots = []
for fig in self.figures_player:
# Empty spots
if self.remaining_pieces != 0:
for i in get_neighbors(fig[0], fig[1]):
if i in self.spots.keys():
if self.spots[i][1] is None:
enemy = False
for j in get_neighbors(i[0], i[1]):
if j in self.spots.keys():
if self.spots[j][1] and self.spots[j][1].team == False:
enemy = True
break
if not enemy:
empty_spots.append(i)
# actual move calc
if self.myMove > 3:
var = self.connections_checker(self.spots, fig)
if var:
animal = self.spots[fig][1]
arr = animal.get_valid_jumps(self.spots, fig)
if (len(arr) > 0):
moveable[fig] = arr
return moveable, empty_spots
def placer(self, empty):
animal = random.choice([a for a, v in self.myPieces.items() if v != 0])
spot = random.choice(empty)
return [animal, None, None, spot[0], spot[1]]
def mover(self, moveable):
animal = random.choice([a for a in moveable.keys()])
jump = random.choice(moveable[animal])
return self.spots[animal][1].get_piece_info(self.myColorIsUpper) + [jump[0], jump[1]]
def move(self):
if self.myMove > 80:
return []
self.translate_board()
free = [6]
if self.bee:
free = [1 for n in get_neighbors(
self.bee.p, self.bee.q) if n in self.spots.keys() and self.spots[n][1] == None]
if sum(free) <= 0:
return []
if self.myMove == 0:
animal = random.choice(list(self.myPieces.keys()))
if self.all_remaining == 0:
return [animal, None, None, 3, 6]
else:
choice = random.choice(get_neighbors(3, 6))
return [animal, None, None, choice[0], choice[1]]
moveable, empty = self.get_movable_and_empties()
if len(moveable) == 0 and len(empty) == 0: # movement impossible
return []
if self.bee == None and (self.myMove == 3 or random.choice([True, False])) and self.myMove<=3:
spot = random.choice(empty)
return ["Q" if self.myColorIsUpper else 'q', None, None, spot[0], spot[1]]
if self.myMove <= 3:
return self.placer(empty)
if self.bee:
if (random.choice([True, False]) or len(moveable) == 0) and self.remaining_pieces:
return self.placer(empty)
else:
return self.mover(moveable)
return moveable
def updatePlayers(move, activePlayer, passivePlayer):
"""write move made by activePlayer player
this method assumes that all moves are correct, no checking is made
"""
if len(move) == 0:
return
animal, p, q, newp, newq = move
if p == None and q == None:
# placing new animal
activePlayer.myPieces[animal] -= 1
passivePlayer.rivalPieces = activePlayer.myPieces.copy()
else:
# just moving animal
# delete its old position
activePlayer.board[p][q] = activePlayer.board[p][q][:-1]
passivePlayer.board[p][q] = passivePlayer.board[p][q][:-1]
activePlayer.board[newp][newq] += animal
passivePlayer.board[newp][newq] += animal
if __name__ == "__main__":
boardSize = 13
smallFigures = {
"q": 1,
"a": 2,
"b": 2,
"s": 2,
"g": 2,
} # key is animal, value is how many is available for placing
bigFigures = {
figure.upper(): smallFigures[figure] for figure in smallFigures
} # same, but with upper case
P1 = Player("player1", False, 13, smallFigures, bigFigures)
P2 = Player("player2", True, 13, bigFigures, smallFigures)
filename = "begin.png"
P1.saveImage(filename)
moveIdx = 0
while True:
move = P1.move()
print("P1 returned", move)
updatePlayers(move, P1, P2) # update P1 and P2 according to the move
filename = "moves/move-{:03d}-player1.png".format(moveIdx)
P1.saveImage(filename)
move = P2.move()
print("P2 returned", move)
updatePlayers(move, P2, P1) # update P2 and P1 according to the move
filename = "moves/move-{:03d}-player2.png".format(moveIdx)
P1.saveImage(filename)
moveIdx += 1
P1.myMove = moveIdx
P2.myMove = moveIdx
if moveIdx > 50:
print("End of the test game")
break