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