import pygame import numpy as np import random import math import cProfile class MonteCarlo: def __init__(self, simulations=1000): # 1000 Simulationen self.simulations = simulations def get_move(self, board, player): moves = self.get_close_moves(board) if len(moves) == 0: return None best_move = None best_win_rate = -float('inf') for move in moves: wins = 0 for _ in range(self.simulations): wins += self._simulate(board.copy(), move, player) wins *= -1 print("move="+str(move) + " wins="+str(wins)) win_rate = wins / self.simulations if win_rate > best_win_rate: best_win_rate = win_rate best_move = move return best_move def _simulate(self, board, move, player): board[move[0], move[1]] = player current_player = -player # Monte Carlo Search spielt eigentlich bis zum Ende #while True: # das ginge mit dieser Endlosschleife # stattdessen werden hier nur die nächsten 10 Spielzüge # betrachtet for i in range(15): winner = Gomoku.has_won_static(board, move[0], move[1]) if winner != 0: return winner move = self.get_rand_move(board) board[move[0], move[1]] = current_player current_player *= -1 return 0 def get_rand_move(self, board): GRID_SIZE = len(board) positions = np.argwhere(board != 0) while True: pos = random.choice(positions) # Ein Schritt in zufällige Richtung dx = random.choice([-1, 0, 1]) dy = random.choice([-1, 0, 1]) new_x, new_y = pos[1] + dx, pos[0] + dy if 0 <= new_x < GRID_SIZE and 0 <= new_y < GRID_SIZE and board[new_y, new_x] == 0: return(new_y, new_x) # statt alle Spielzüge zu betrachten, werden hier nur die # betrachtet, die direkt neben einem anderen Stein liegen def get_close_moves(self, board): GRID_SIZE = len(board) occupied_positions = np.argwhere(board != 0) possible_moves = set() # Definiere alle möglichen Richtungen (oben, unten, links, rechts, diagonal) directions = [(1, 0), (-1, 0), (0, 1), (0, -1), (1, 1), (-1, -1), (1, -1), (-1, 1)] for pos in occupied_positions: for dx, dy in directions: new_x, new_y = pos[1] + dx, pos[0] + dy if 0 <= new_x < GRID_SIZE and 0 <= new_y < GRID_SIZE and board[new_y, new_x] == 0: possible_moves.add((new_y, new_x)) if not possible_moves: # Wenn keine möglichen Züge vorhanden sind return None return list(possible_moves) class Gomoku: GRID_SIZE = 15 CELL_SIZE = 40 OFFSET = 20 BOARD_SIZE = (GRID_SIZE-1) * CELL_SIZE + 2 * OFFSET STONE_RADIUS = CELL_SIZE // 2 - 5 BLACK = (0, 0, 0) WHITE = (255, 255, 255) BACKGROUND = (220, 180, 140) def __init__(self): pygame.init() self.screen = pygame.display.set_mode((self.BOARD_SIZE, self.BOARD_SIZE)) pygame.display.set_caption("Gomoku") self.clock = pygame.time.Clock() self.board = np.zeros((self.GRID_SIZE, self.GRID_SIZE), dtype=int) self.current_player = 1 def draw_board(self): self.screen.fill(self.BACKGROUND) for i in range(self.GRID_SIZE): pygame.draw.line(self.screen, self.BLACK, (i * self.CELL_SIZE + self.OFFSET, self.OFFSET), (i * self.CELL_SIZE + self.OFFSET, self.BOARD_SIZE - self.CELL_SIZE + self.OFFSET)) pygame.draw.line(self.screen, self.BLACK, (self.OFFSET, i * self.CELL_SIZE + self.OFFSET), (self.BOARD_SIZE - self.CELL_SIZE + self.OFFSET, i * self.CELL_SIZE + self.OFFSET)) def draw_stones(self): for y in range(self.GRID_SIZE): for x in range(self.GRID_SIZE): if self.board[y, x] != 0: color = self.BLACK if self.board[y, x] == 1 else self.WHITE pygame.draw.circle(self.screen, color, (x * self.CELL_SIZE + self.OFFSET, y * self.CELL_SIZE + self.OFFSET), self.STONE_RADIUS) @staticmethod def has_won_static(board, x, y): # gibt 0=keiner gewonnen 1=schwarz gewonnen oder -1=weiß gewonnen zurück # Der Stein, der zuletzt gespielt wurde last_stone = board[y][x] if last_stone == 0: return 0 # Kein Stein an dieser Position # Alle vier Richtungen überprüfen: horizontal, vertikal, beide Diagonalen directions = [(1, 0), (0, 1), (1, 1), (1, -1)] GRID_SIZE = len(board) for dx, dy in directions: count = 1 # Zähler für die Anzahl der zusammenhängenden Steine # Überprüfe in einer Richtung for i in range(1, 5): new_x, new_y = x + dx * i, y + dy * i if 0 <= new_x < GRID_SIZE and 0 <= new_y < GRID_SIZE and board[new_y][new_x] == last_stone: count += 1 else: break # Überprüfe in der entgegengesetzten Richtung for i in range(1, 5): new_x, new_y = x - dx * i, y - dy * i if 0 <= new_x < GRID_SIZE and 0 <= new_y < GRID_SIZE and board[new_y][new_x] == last_stone: count += 1 else: break # Überprüfe, ob die Reihe lang genug ist if count >= 5: return board[y][x] return 0 def run(self): mc = MonteCarlo() winner = 0 running = True xx, yy = 0, 0 while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # Mensch spielt schwarz if event.type == pygame.MOUSEBUTTONDOWN and self.current_player == 1: x, y = event.pos xx, yy = round((x - self.OFFSET) / self.CELL_SIZE), round((y - self.OFFSET) / self.CELL_SIZE) if 0 <= xx < self.GRID_SIZE and 0 <= yy < self.GRID_SIZE and self.board[yy, xx] == 0: self.board[yy, xx] = self.current_player self.current_player *= -1 self.draw_board() self.draw_stones() pygame.display.flip() if winner == 0: winner = Gomoku.has_won_static(self.board, xx, yy) # Monte Carlo spielt weiß if self.current_player == -1 and winner==0: move = mc.get_move(self.board, self.current_player) if move: self.board[move[0], move[1]] = self.current_player yy = move[0] xx = move[1] self.current_player *= -1 self.draw_board() self.draw_stones() pygame.display.flip() if winner == 0: winner = Gomoku.has_won_static(self.board, xx, yy) if winner == 1: print("Schwarz hat gewonnen.") if winner == -1: print("Weiß hat gewonnen.") pygame.quit() if __name__ == "__main__": game = Gomoku() #cProfile.run('game.run()') game.run()