aboutsummaryrefslogtreecommitdiffstats
path: root/python/GameState.py
blob: eed8f3601a1afaf4a035e7a33ca1a35f2c4adf92 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
from random import choice

# Based on https://github.com/DieterBuys/mcts-player/

class GameState(object):

    def __init__(self):
        self.next_turn_player = 1
        self.player = None

    @property
    def game_result(self):
        return None

    def get_moves(self):
        return set()

    def get_random_move(self):
        moves = self.get_moves()
        return choice(tuple(moves)) if moves != set() else None

    def play_move(self, move):
        pass


class DotsAndBoxesState(GameState):
    def __init__(self, nb_rows, nb_cols, player):
        super(DotsAndBoxesState, self).__init__()

        self.nb_rows = nb_rows
        self.nb_cols = nb_cols
        rows = []
        for ri in range(nb_rows + 1):
            columns = []
            for ci in range(nb_cols + 1):
                columns.append({"v": 0, "h": 0})
            rows.append(columns)
        self.board = rows

        self.score = {1: 0, 2: 0}
        self.player = player
        print("Player: ", player)

    @property
    def game_result(self):
        def game_decided(nb_cols, nb_rows, scoreP, scoreO):
            # the game is decided if the winner is already known even before the game is ended
            # you're guaranteed to win the game if you have more than halve of the total points that can be earned
            total_points = nb_rows * nb_cols
            if scoreP > total_points // 2 or scoreO > total_points // 2:
                return True
            else:
                return False

        # check if the board is full, then decide based on score
        free_lines = self.get_moves()
        player = self.player
        opponent = self.player % 2 + 1

        if not game_decided(self.nb_cols, self.nb_rows, self.score[player], self.score[opponent]) and len(free_lines) > 0:
            return None
        elif self.score[player] > self.score[opponent]:
            return 1
        elif self.score[player] < self.score[opponent]:
            return 0
        else:
            return 0.5

    def get_moves(self):
        free_lines = []
        for ri in range(len(self.board)):
            row = self.board[ri]
            for ci in range(len(row)):
                cell = row[ci]
                if ri < (len(self.board) - 1) and cell["v"] == 0:
                    free_lines.append((ri, ci, "v"))
                if ci < (len(row) - 1) and cell["h"] == 0:
                    free_lines.append((ri, ci, "h"))
        return set(free_lines)

    def play_move(self, move):
        r, c, o = move
        assert move in self.get_moves()

        # check if this move makes a box
        makes_box = False
        if o == "h":
            if r - 1 >= 0:
                # check above
                if self.board[r-1][c]["h"] != 0 and self.board[r-1][c]["v"] != 0 and self.board[r-1][c+1]["v"] != 0:
                    makes_box = True
                    self.score[self.next_turn_player] += 1
            if r + 1 <= self.nb_rows:
                # check below
                if self.board[r+1][c]["h"] != 0 and self.board[r][c]["v"] != 0 and self.board[r][c+1]["v"] != 0:
                    makes_box = True
                    self.score[self.next_turn_player] += 1

        elif o == "v":
            if c - 1 >= 0:
                # check left
                if self.board[r][c-1]["v"] != 0 and self.board[r][c-1]["h"] != 0 and self.board[r+1][c-1]["h"] != 0:
                    makes_box = True
                    self.score[self.next_turn_player] += 1

            if c + 1 <= self.nb_cols:
                # check right
                if self.board[r][c+1]["v"] != 0 and self.board[r][c]["h"] != 0 and self.board[r+1][c]["h"] != 0:
                    makes_box = True
                    self.score[self.next_turn_player] += 1


        # register move
        self.board[r][c][o] = self.next_turn_player

        if not makes_box:
            # switch turns
            self.next_turn_player = self.next_turn_player % 2 + 1

    def __repr__(self):
        str = ""
        for r in range(self.nb_rows + 1):
            for o in ["h", "v"]:
                for c in range(self.nb_cols + 1):
                    if o == "h":
                        str += "."
                        if c != self.nb_cols:
                            if self.board[r][c][o] == 0:
                                str += "  "
                            else:
                                str += "__"
                        else:
                            str += "\n"
                    elif o == "v":
                        if r != self.nb_rows:
                            if self.board[r][c][o] == 0:
                                str += " "
                            else:
                                str += "|"
                        if c != self.nb_cols:
                            str += "  "
                        else:
                            str += "\n"
        return str