From b63ef62551f26e6c65ab81b608d03d303acf6321 Mon Sep 17 00:00:00 2001
From: Xiaofei Wang <xiaofei@student.unimelb.edu.au>
Date: Wed, 11 Mar 2020 21:05:40 +1100
Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=B0=E6=96=87=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 AZUL Learn Opponent/utils.py | 258 +++++++++++++++++++++++++++++++++++
 1 file changed, 258 insertions(+)
 create mode 100644 AZUL Learn Opponent/utils.py

diff --git a/AZUL Learn Opponent/utils.py b/AZUL Learn Opponent/utils.py
new file mode 100644
index 0000000..4740df1
--- /dev/null
+++ b/AZUL Learn Opponent/utils.py	
@@ -0,0 +1,258 @@
+# Written by Michelle Blom, 2019
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+#
+from enum import IntEnum
+
+# There are 5 types of tiles in the game, each differentiated by colour.
+class Tile(IntEnum):
+    BLUE = 0
+    YELLOW = 1
+    RED = 2
+    BLACK = 3
+    WHITE = 4
+
+# There are 2 types of moves a player can perform in the game
+class Move(IntEnum):
+    TAKE_FROM_FACTORY = 1
+    TAKE_FROM_CENTRE = 2
+
+# Bundle together a player's activity in the game for use in
+# updating a policy
+class PlayerTrace:
+    def __init__(self, pid):
+        # Player ID
+        self.id = pid
+
+        # Round by round move history
+        self.moves = []
+    
+        # Round by round scores
+        self.round_scores = []
+
+        # Bonus scores
+        self.bonuses = 0
+
+    def StartRound(self):
+        self.moves.append(list())
+        self.round_scores.append(0)
+        
+
+# Structure recording the number, type, and destination of tiles 
+# collected by a player. Note that the sum of 'num_to_pattern_line'
+# and 'num_to_floor_line' must equal 'number'.
+class TileGrab:
+    def __init__(self):
+        self.tile_type = -1
+        self.number = 0
+        self.pattern_line_dest = -1
+        self.num_to_pattern_line = 0
+        self.num_to_floor_line = 0
+
+    def __str__(self):
+        str_tile = str(self.tile_type)[5] if str(self.tile_type)[-1] != 'K' else 'K'
+        string = '->{} {}({})'.format(self.pattern_line_dest, str_tile, self.number)
+        return string
+
+def SameTG(tg1, tg2):
+    if tg1.tile_type != tg2.tile_type:
+        return False
+
+    if tg1.number != tg2.number:
+        return False
+
+    if tg1.pattern_line_dest != tg2.pattern_line_dest:
+        return False
+
+    if tg1.num_to_pattern_line != tg2.num_to_pattern_line:
+        return False
+
+    if tg1.num_to_floor_line != tg2.num_to_floor_line:
+        return False
+
+    return True
+
+def ValidMove(c, moves):
+    for m in moves:
+        if c[0] == m[0] and c[1] == m[1] and SameTG(c[2],m[2]):
+            return True
+    return False
+
+def TileToString(tile):
+    if tile == Tile.RED:
+        return "red (R)"
+    elif tile == Tile.BLUE:
+        return "blue (B)"
+    elif tile == Tile.WHITE:
+        return "white (W)"
+    elif tile == Tile.BLACK:
+        return "black (K)"
+    elif tile == Tile.YELLOW:
+        return "yellow (Y)"
+    else:
+        return "unknown (U)"
+
+def TileToShortString(tile):
+    if tile == Tile.RED:
+        return "R"
+    elif tile == Tile.BLUE:
+        return "B"
+    elif tile == Tile.WHITE:
+        return "W"
+    elif tile == Tile.BLACK:
+        return "K"
+    elif tile == Tile.YELLOW:
+        return "Y"
+    else:
+        return "U"
+
+def B2S(binary):
+    if binary == 0:
+        return "_"
+    else:
+        return "x"
+
+def MoveToString(player_id, move):
+    tg = move[2]
+    
+    if move[0] == Move.TAKE_FROM_FACTORY:
+        desc1 = "Player {} takes {} {} tiles from factory {}".format(
+            player_id, tg.number, TileToString(tg.tile_type), move[1]+1)
+
+        if tg.num_to_pattern_line > 0:
+            desc1 += "\n   {} {} placed in pattern line {}".format(
+                tg.num_to_pattern_line, TileToShortString(tg.tile_type),
+                tg.pattern_line_dest+1)
+
+        if tg.num_to_floor_line > 0:
+            desc1 += "\n   {} {} placed in floor line".format(
+                tg.num_to_floor_line,  TileToShortString(tg.tile_type))
+        return desc1
+
+    elif move[0] == Move.TAKE_FROM_CENTRE:
+        desc1 = "Player {} takes {} {} tiles from centre".format(
+            player_id, tg.number, TileToString(tg.tile_type))
+
+        if tg.num_to_pattern_line > 0:
+            desc1 += "\n   {} {} placed in pattern line {}".format(
+                tg.num_to_pattern_line, TileToShortString(tg.tile_type),
+                tg.pattern_line_dest+1)
+
+        if tg.num_to_floor_line > 0:
+            desc1 += "\n   {} {} placed in floor line".format(
+                tg.num_to_floor_line,  TileToShortString(tg.tile_type))
+        return desc1
+           
+    return "Unknown Move"   
+
+
+def PlayerToString(player_id, ps):
+    desc = "Player {} score {}\n".format(player_id, ps.score)
+
+    # Add pattern lines states to description
+    for i in range(ps.GRID_SIZE):
+        filled = ""
+        if ps.lines_tile[i] != -1:
+            tt = ps.lines_tile[i]
+            ts = TileToShortString(tt)
+            num = ps.lines_number[i]
+            filled = ""
+            for j in range(num):
+                filled += "{} ".format(ts)
+            for j in range(num, i+1):
+                filled += "_ "
+
+            for j in range(i+1, 5):
+                filled += "  "
+
+        else:
+            assert ps.lines_number[i] == 0
+            for j in range(i+1):
+                filled += "_ "
+            for j in range(i+1, 5):
+                filled += "  "
+
+        # Add corresponding grid line
+        if i == 0:
+            filled += " {}/B {}/Y {}/R {}/K {}/W\n".format(
+                B2S(ps.grid_state[0][0]), B2S(ps.grid_state[0][1]),
+                B2S(ps.grid_state[0][2]), B2S(ps.grid_state[0][3]),
+                B2S(ps.grid_state[0][4]))
+        elif i == 1:
+            filled += " {}/W {}/B {}/Y {}/R {}/K\n".format(
+                B2S(ps.grid_state[1][0]), B2S(ps.grid_state[1][1]),
+                B2S(ps.grid_state[1][2]), B2S(ps.grid_state[1][3]),
+                B2S(ps.grid_state[1][4]))
+        elif i == 2:
+            filled += " {}/K {}/W {}/B {}/Y {}/R\n".format(
+                B2S(ps.grid_state[2][0]), B2S(ps.grid_state[2][1]),
+                B2S(ps.grid_state[2][2]), B2S(ps.grid_state[2][3]),
+                B2S(ps.grid_state[2][4]))
+        elif i == 3:
+            filled += " {}/R {}/K {}/W {}/B {}/Y\n".format(
+                B2S(ps.grid_state[3][0]), B2S(ps.grid_state[3][1]),
+                B2S(ps.grid_state[3][2]), B2S(ps.grid_state[3][3]),
+                B2S(ps.grid_state[3][4]))
+        elif i == 4:
+            filled += " {}/Y {}/R {}/K {}/W {}/B".format(
+                B2S(ps.grid_state[4][0]), B2S(ps.grid_state[4][1]),
+                B2S(ps.grid_state[4][2]), B2S(ps.grid_state[4][3]),
+                B2S(ps.grid_state[4][4]))
+
+        desc += "    Line {} {}".format(i+1, filled)
+    desc += "\n"
+
+    # Add floor state to description
+    floor = "Floor line "
+    for i in ps.floor:
+        if i == 1:
+            floor += "x "
+        else:
+            floor += "_ " 
+    desc += floor
+    desc += "\n"
+
+    return desc
+
+def TileDisplayToString(td):
+    if td.total == 0:
+        return "No Tiles"
+
+    res = ""
+    for tile in Tile:
+        if td.tiles[tile] > 0:
+            res += "{}x{} ".format(td.tiles[tile], TileToShortString(tile))
+
+    return res    
+
+
+def BoardToString(game_state):
+    # return description of centre pool and factory tiles
+    desc = ""
+    i = 1
+    for fd in game_state.factories:
+        contents = TileDisplayToString(fd)
+        desc += "Factory {} has {}\n".format(i, contents)
+        i += 1
+
+    desc += "Centre has {}".format(
+        TileDisplayToString(game_state.centre_pool)
+    )
+
+    if game_state.first_player_taken:
+        desc += "\n"
+    else:
+        desc += " + first player token (-1)\n"
+         
+    return desc
-- 
GitLab