Eliguli / NYU-CAS-Meows-Corp-Association

1 stars 0 forks source link

Surakarta Strategies #4

Open Eliguli opened 3 months ago

Eliguli commented 3 months ago
class Offset:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def get_x_offset(self):
        return self.x

    def get_y_offset(self):
        return self.y

class Node:
    def __init__(self, column, row, status=0):
        self.column = column
        self.row = row
        self.status = status
        self.railway_id = None

    def __str__(self):
        return f"column: {self.column} row: {self.row} status: {self.status} rail {self.railway_id}"

    def is_same_position(self, node):
        return self.row == node.row and self.column == node.column

class GridDrawer:
    COLORS = {
        "red": 41,
        "yellow": 43,
        "green": 42,
        "blue": 44
    }

    def __init__(self, game_grid):
        self.game_grid = game_grid
        self.highlighted = []
        self.highlight_color = []

    def highlight(self, column, row, color):
        self.highlight_node(self.game_grid.find_node(column, row), color)

    def highlight_node(self, node, color):
        self.highlighted.append(node)
        self.highlight_color.append(str(self.COLORS[color]))

    def highlight_nodes(self, nodes, color):
        for node in nodes:
            self.highlight_node(node, color)

    def clear_all_highlight(self):
        self.highlighted.clear()
        self.highlight_color.clear()

    def clear_highlight(self, node):
        if node in self.highlighted:
            index = self.highlighted.index(node)
            self.highlight_color.pop(index)
            self.highlighted.remove(node)

    def print_grid(self):
        for i in range(self.game_grid.total_column + 1):
            for j in range(self.game_grid.total_row + 1):
                print("\033[0m", end="")
                if self.game_grid.find_node(j, i) in self.highlighted:
                    index = self.highlighted.index(self.game_grid.find_node(j, i))
                    print(f"\033[{self.highlight_color[index]}m", end="")
                node_status = self.game_grid.find_node(j, i).status
                if node_status == 0:
                    print("\033[37m. ", end="")
                elif node_status == 1:
                    print("\033[31m* ", end="")
                elif node_status == 2:
                    print("\033[34m* ", end="")
            print("\033[0m")
            print("")

class Grid:
    DIRECTIONS8 = [
        Offset(-1, -1), Offset(-1, 0), Offset(-1, 1),
        Offset(0, -1), Offset(0, 1),
        Offset(1, -1), Offset(1, 0), Offset(1, 1)
    ]

    def __init__(self, column_size, row_size):
        self.column_size = column_size
        self.row_size = row_size
        self.total_column = column_size - 1
        self.total_row = row_size - 1
        self.nodes = [[Node(i, j) for j in range(row_size)] for i in range(column_size)]
        self.railways = []

        for i in range(column_size):
            self.nodes[i][0].status = 1
            self.nodes[i][1].status = 1
            self.nodes[i][4].status = 2
            self.nodes[i][5].status = 2

    def is_legal_position(self, column, row):
        return 0 <= column <= self.total_column and 0 <= row <= self.total_row

    def find_node(self, column, row):
        return self.nodes[column][row]

    def mirror_node(self, column, row, mode):
        if mode == 1:
            return self.find_node(self.total_column - column, row)
        elif mode == 2:
            return self.find_node(column, self.total_row - row)
        elif mode == 3:
            return self.find_node(self.total_row - row, self.total_column - column)
        elif mode == 4:
            return self.find_node(row, column)
        elif mode == 5:
            return self.find_node(self.total_row - row, column)
        else:
            return self.nodes[column][row]

    def check_game_status(self):
        for i in range(self.total_column + 1):
            for j in range(self.total_row + 1):
                if self.find_node(i, j).status != 0:
                    return False
        return True

    def mirror_node_by_instance(self, node, mode):
        return self.mirror_node(node.column, node.row, mode)

    def get_available_path(self, this_node):
        normal_move = []
        capture_move = []
        protected_move = []
        result = []
        visited = {}

        for this_rail in self.railways:
            for node in this_rail:
                if node.is_same_position(this_node):
                    for i in range(len(this_rail)):
                        if this_rail[i].column == this_node.column and this_rail[i].row == this_node.row:
                            j = i
                            j = (j + 1) % len(this_rail)
                            while j not in visited and this_rail[j].status == 0:
                                normal_move.append(this_rail[j])
                                visited[j] = 114514
                                j = (j + 1) % len(this_rail)
                            if j in visited:
                                continue
                            elif this_rail[j].status == this_node.status and this_rail[j] != this_node:
                                protected_move.append(this_rail[j])
                            elif this_rail[j].status != this_node.status and this_rail[j].status != 0:
                                capture_move.append(this_rail[j])

                            j = i
                            j = (j - 1 + len(this_rail)) % len(this_rail)
                            while j not in visited and this_rail[j].status == 0:
                                normal_move.append(this_rail[j])
                                visited[j] = 1919810
                                j = (j - 1 + len(this_rail)) % len(this_rail)
                            if j in visited:
                                continue
                            elif this_rail[j].status == this_node.status and this_rail[j] != this_node:
                                protected_move.append(this_rail[j])
                            elif this_rail[j].status != this_node.status and this_rail[j].status != 0:
                                capture_move.append(this_rail[j])
                    break

        for this_offset in self.DIRECTIONS8:
            if self.is_legal_position(this_node.column + this_offset.x, this_node.row + this_offset.y):
                new_node = self.find_node(this_node.column + this_offset.x, this_node.row + this_offset.y)
                if new_node not in visited:
                    if new_node.status == 0:
                        normal_move.append(new_node)
                    elif new_node.status == this_node.status:
                        protected_move.append(new_node)
                    else:
                        capture_move.append(new_node)

        result.append(normal_move)
        result.append(capture_move)
        result.append(protected_move)
        return result

    def generate_surakarta_path(self):
        for i in range(1, self.total_column // 2 + 1):
            railway = []
            for j in range(self.total_column + 1):
                node = self.find_node(j, i)
                node.railway_id = i - 1
                railway.append(node)
            for j in range(3 * self.total_column + 3):
                mirrored_node = self.mirror_node_by_instance(railway[j], 5)
                mirrored_node.railway_id = i - 1
                railway.append(mirrored_node)
            self.railways.append(railway)

    def move_chess(self, from_node, to_node):
        if from_node.status == 0 or to_node.status == from_node.status:
            return False
        else:
            to_node.status = from_node.status
            from_node.status = 0
            return True

class GamePlayer:
    def __init__(self, game_grid, name, side):
        self.game_grid = game_grid
        self.name = name
        self.side = side

    def next_move(self):
        return True

class ExamplePlayer(GamePlayer):
    def __init__(self, game_grid, name, side):
        super().__init__(game_grid, name, side)
        self.grid_drawer = GridDrawer(game_grid)
        self.rand = random.Random()

    class StatNode(Node):
        def __init__(self, column, row, node=None):
            if node:
                super().__init__(node.column, node.row, node.status)
                self.railway_id = node.railway_id
            else:
                super().__init__(column, row)
            self.is_protected = False
            self.is_captured = False

        def __str__(self):
            return f"column: {self.column} row: {self.row} status: {self.status} rail {self.railway_id} protected {'yes' if self.is_protected else 'no'} captured {'yes' if self.is_captured else 'no'}"

    def next_move(self):
        node_status = [[self.StatNode(i, j, self.game_grid.find_node(i, j)) for j in range(self.game_grid.total_row + 1)] for i in range(self.game_grid.total_column + 1)]
        moves = [[] for _ in range(5)]

        for i in  range(self.game_grid.total_column + 1):
            for j in range(self.game_grid.total_row + 1):
                node = self.game_grid.find_node(i, j)
                if node.status != self.side and node.status != 0:
                    res = self.game_grid.get_available_path(node)
                    for this_node in res[2] + res[0] + res[1]:
                        node_status[this_node.column][this_node.row].is_protected = True

        for i in range(self.game_grid.total_column + 1):
            for j in range(self.game_grid.total_row + 1):
                node = node_status[i][j]
                if not node.is_protected and node.status != self.side and node.status != 0:
                    moves[0].append(node)
                elif node.is_protected and node.status != self.side and node.status != 0:
                    moves[1].append(node)
                elif not node.is_protected and node.status == 0:
                    moves[2].append(node)
                elif node.is_protected and node.status == 0:
                    moves[3].append(node)

        for move in moves:
            for this_node in move:
                if this_node.status != self.side:
                    new_node = Node(this_node.column, this_node.row)
                    new_node.status = -1
                    new_node.railway_id = this_node.railway_id
                    possible_start = self.game_grid.get_available_path(new_node)
                    random.shuffle(possible_start[1])
                    possible_nodes = [start_node for start_node in possible_start[1] if start_node.status == self.side]
                    if possible_nodes:
                        start_node = possible_nodes[int(len(possible_nodes) * self.rand.random())]
                        return self.game_grid.move_chess(self.game_grid.find_node(start_node.column, start_node.row), self.game_grid.find_node(this_node.column, this_node.row))
        return False

class ExamplePlayer2(GamePlayer):
    def __init__(self, game_grid, name, side):
        super().__init__(game_grid, name, side)
        self.grid_drawer = GridDrawer(game_grid)
        self.rand = random.Random()

    def next_move(self):
        my_nodes = [self.game_grid.find_node(i, j) for i in range(self.game_grid.total_column + 1) for j in range(self.game_grid.total_row + 1) if self.game_grid.find_node(i, j).status == self.side]
        random.shuffle(my_nodes)

        for node in my_nodes:
            all_possible_end = self.game_grid.get_available_path(node)
            possible_ends = all_possible_end[1]
            random.shuffle(possible_ends)
            if possible_ends:
                return self.game_grid.move_chess(node, possible_ends[0])

        for node in my_nodes:
            all_possible_end = self.game_grid.get_available_path(node)
            possible_ends = all_possible_end[0]
            random.shuffle(possible_ends)
            if possible_ends:
                return self.game_grid.move_chess(node, possible_ends[0])

        return False

if __name__ == "__main__":
    import random

    p1_win_count = 0

    for _ in range(5000):
        game_grid = Grid(6, 6)
        game_grid.generate_surakarta_path()
        player1 = ExamplePlayer(game_grid, "p1", 1)
        player2 = ExamplePlayer2(game_grid, "p2", 2)
        for _ in range(100):
            if not player1.next_move():
                break
            if game_grid.check_game_status():
                p1_win_count += 1
                break
            if not player2.next_move():
                p1_win_count += 1
                break
            if game_grid.check_game_status():
                break

    print(p1_win_count / 5000)

if __name__ == "__main__":
    import random

    # Initialize the game grid and players
    game_grid = Grid(6, 6)
    game_grid.generate_surakarta_path()
    player1 = ExamplePlayer(game_grid, "p1", 1)
    player2 = ExamplePlayer2(game_grid, "p2", 2)
    grid_drawer = GridDrawer(game_grid)

    # Simulate the game
    for _ in range(100):
        grid_drawer.print_grid()
        print()
        if not player1.next_move():
            print("Player 2 wins!")
            break
        if game_grid.check_game_status():
            print("Player 1 wins!")
            break
        grid_drawer.print_grid()
        print()
        if not player2.next_move():
            print("Player 1 wins!")
            break
        if game_grid.check_game_status():
            print("Player 2 wins!")
            break

    # Print final grid state
        print()
        grid_drawer.print_grid()

image image