From 52ddc4824532354870b22a9851acad2c5a37139f Mon Sep 17 00:00:00 2001
From: Xiaofei Wang <xiaofei@student.unimelb.edu.au>
Date: Wed, 11 Mar 2020 20:59:06 +1100
Subject: [PATCH] new file
---
AZUL/model.py | 753 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 753 insertions(+)
create mode 100644 AZUL/model.py
diff --git a/AZUL/model.py b/AZUL/model.py
new file mode 100644
index 0000000..3d76ab6
--- /dev/null
+++ b/AZUL/model.py
@@ -0,0 +1,753 @@
+# 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 state_monitor import set_round_recorder, get_round_recorder, get_pre_r
+from utils import *
+
+import numpy
+import random
+import abc
+import copy
+
+
+# We use the tile display class to represent both factory displays and
+# the pool of tiles in the centre of the playing area.
+class TileDisplay:
+ def __init__(self):
+ # Map between tile colour and number in the display
+ self.tiles = {}
+
+ # Total number of tiles in the display
+ self.total = 0
+
+ for tile in Tile:
+ self.tiles[tile] = 0
+
+ def RemoveTiles(self, number, tile_type):
+ assert number > 0
+ assert tile_type in Tile
+ assert tile_type in self.tiles
+
+ self.tiles[tile_type] -= number
+ self.total -= number
+
+ assert self.tiles[tile_type] >= 0
+ assert self.total >= 0
+
+ def AddTiles(self, number, tile_type):
+ assert number > 0
+ assert tile_type in Tile
+ assert tile_type in self.tiles
+
+ self.tiles[tile_type] += number
+ self.total += number
+
+
+# We use the PlayerState class to represent a player's game state:
+# their score; the state of their pattern lines; the state of their
+# wall grid; and their floor line.
+class PlayerState:
+ GRID_SIZE = 5
+ FLOOR_SCORES = [-1, -1, -2, -2, -2, -3, -3]
+ ROW_BONUS = 2
+ COL_BONUS = 7
+ SET_BONUS = 10
+
+ def __init__(self, _id):
+ self.id = _id
+ self.score = 0
+ self.lines_number = [0] * self.GRID_SIZE
+ self.lines_tile = [-1] * self.GRID_SIZE
+
+ # Use for evaluation
+ self.score_without_bouns = 0
+ self.penalties = 0
+ self.rows_finished = 0
+ self.columns_finished = 0
+ self.sets_finished = 0
+ self.bonus_received = 0
+
+ self.player_trace = PlayerTrace(_id)
+
+ # self.grid_scheme = [
+ # [Tile.BLUE,Tile.YELLOW,Tile.RED,Tile.BLACK,Tile.WHITE],
+ # [Tile.WHITE,Tile.BLUE,Tile.YELLOW,Tile.RED,Tile.BLACK],
+ # [Tile.BLACK,Tile.WHITE,Tile.BLUE,Tile.YELLOW,Tile.RED],
+ # [Tile.RED,Tile.BLACK,Tile.WHITE,Tile.BLUE,Tile.YELLOW],
+ # [Tile.YELLOW,Tile.RED,Tile.BLACK,Tile.WHITE,Tile.BLUE]
+ # ]
+ self.grid_scheme = numpy.zeros((self.GRID_SIZE, self.GRID_SIZE))
+ self.grid_scheme[0][Tile.BLUE] = 0
+ self.grid_scheme[1][Tile.BLUE] = 1
+ self.grid_scheme[2][Tile.BLUE] = 2
+ self.grid_scheme[3][Tile.BLUE] = 3
+ self.grid_scheme[4][Tile.BLUE] = 4
+
+ self.grid_scheme[1][Tile.WHITE] = 0
+ self.grid_scheme[2][Tile.WHITE] = 1
+ self.grid_scheme[3][Tile.WHITE] = 2
+ self.grid_scheme[4][Tile.WHITE] = 3
+ self.grid_scheme[0][Tile.WHITE] = 4
+
+ self.grid_scheme[2][Tile.BLACK] = 0
+ self.grid_scheme[3][Tile.BLACK] = 1
+ self.grid_scheme[4][Tile.BLACK] = 2
+ self.grid_scheme[0][Tile.BLACK] = 3
+ self.grid_scheme[1][Tile.BLACK] = 4
+
+ self.grid_scheme[3][Tile.RED] = 0
+ self.grid_scheme[4][Tile.RED] = 1
+ self.grid_scheme[0][Tile.RED] = 2
+ self.grid_scheme[1][Tile.RED] = 3
+ self.grid_scheme[2][Tile.RED] = 4
+
+ self.grid_scheme[4][Tile.YELLOW] = 0
+ self.grid_scheme[0][Tile.YELLOW] = 1
+ self.grid_scheme[1][Tile.YELLOW] = 2
+ self.grid_scheme[2][Tile.YELLOW] = 3
+ self.grid_scheme[3][Tile.YELLOW] = 4
+
+ # Matrix representing state of the player's grid (ie. which
+ # slots have tiles on them -- 1s -- and which don't -- 0s).
+ self.grid_state = numpy.zeros((self.GRID_SIZE, self.GRID_SIZE))
+
+ # State of the player's floor line, a 1 indicates there is
+ # a tile sitting in that position in their floor line.
+ self.floor = [0, 0, 0, 0, 0, 0, 0]
+ self.floor_tiles = []
+
+ # Record of the number of tiles of each colour the player
+ # has placed in their grid (useful for end-game scoring)
+ self.number_of = {}
+ for tile in Tile:
+ self.number_of[tile] = 0
+
+ # Add given tiles to the player's floor line. After calling this
+ # method, 'tiles' will contain tiles that could not be added to
+ # the player's floor line.
+ def AddToFloor(self, tiles):
+ number = len(tiles)
+ for i in range(len(self.floor)):
+ if self.floor[i] == 0:
+ self.floor[i] = 1
+ tt = tiles.pop(0)
+ self.floor_tiles.append(tt)
+ number -= 1
+ if number == 0:
+ break
+
+ # Add given number of given tile type to the specified pattern line
+ def AddToPatternLine(self, line, number, tile_type):
+ assert line >= 0 and line < self.GRID_SIZE
+
+ assert (self.lines_tile[line] == -1 or
+ self.lines_tile[line] == tile_type)
+
+ self.lines_number[line] += number
+ self.lines_tile[line] = tile_type
+
+ assert self.lines_number[line] <= line + 1
+
+ # Assign first player token to this player
+
+ def GiveFirstPlayerToken(self):
+ for i in range(len(self.floor)):
+ if self.floor[i] == 0:
+ self.floor[i] = 1
+ break
+
+ # Compute number of completed rows in the player's grid
+ def GetCompletedRows(self):
+ completed = 0
+ for i in range(self.GRID_SIZE):
+ allin = True
+ for j in range(self.GRID_SIZE):
+ if self.grid_state[i][j] == 0:
+ allin = False
+ break
+ if allin:
+ completed += 1
+ return completed
+
+ # Compute number of completed columns in the player's grid
+ def GetCompletedColumns(self):
+ completed = 0
+ for i in range(self.GRID_SIZE):
+ allin = True
+ for j in range(self.GRID_SIZE):
+ if self.grid_state[j][i] == 0:
+ allin = False
+ break
+ if allin:
+ completed += 1
+ return completed
+
+ # Compute the number of completed tile sets in the player's grid
+ def GetCompletedSets(self):
+ completed = 0
+ for tile in Tile:
+ if self.number_of[tile] == self.GRID_SIZE:
+ completed += 1
+ return completed
+
+ # Return the set of moves available to this player given the
+ # current game state.
+ def GetAvailableMoves(self, game_state):
+ moves = []
+
+ # Look at each factory display with available tiles
+ fid = 0
+ for fd in game_state.factories:
+ # Look at each available tile set
+ for tile in Tile:
+ num_avail = fd.tiles[tile]
+
+ if num_avail == 0:
+ continue
+
+ # A player can always take tiles, as they can be
+ # added to their floor line (if their floor line is
+ # full, the extra tiles are placed in the used bag).
+
+ # First look through each pattern line, create moves
+ # that place the tiles in each appropriate line (with
+ # those that cannot be placed added to the floor line).
+ for i in range(self.GRID_SIZE):
+ # Can tiles be added to pattern line i?
+ if self.lines_tile[i] != -1 and \
+ self.lines_tile[i] != tile:
+ # these tiles cannot be added to this pattern line
+ continue
+
+ # Is the space on the grid for this tile already
+ # occupied?
+ grid_col = int(self.grid_scheme[i][tile])
+ if self.grid_state[i][grid_col] == 1:
+ # It is, so we cannot place this tile type
+ # in this pattern line!
+ continue
+
+ slots_free = (i + 1) - self.lines_number[i]
+ tg = TileGrab()
+ tg.number = num_avail
+ tg.tile_type = tile
+ tg.pattern_line_dest = i
+ tg.num_to_pattern_line = min(num_avail, slots_free)
+ tg.num_to_floor_line = tg.number - tg.num_to_pattern_line
+
+ moves.append((Move.TAKE_FROM_FACTORY, fid, tg))
+
+ # Default move is to place all the tiles in the floor line
+ tg = TileGrab()
+ tg.number = num_avail
+ tg.tile_type = tile
+ tg.num_to_floor_line = tg.number
+ moves.append((Move.TAKE_FROM_FACTORY, fid, tg))
+
+ fid += 1
+
+ # Alternately, the player could take tiles from the centre pool.
+ # Note that we do not include the first player token in the
+ # collection of tiles recorded in each TileGrab. This is managed
+ # by the game running class.
+ for tile in Tile:
+ # Number of tiles of this type in the centre
+ num_avail = game_state.centre_pool.tiles[tile]
+
+ if num_avail == 0:
+ continue
+
+ # First look through each pattern line, create moves
+ # that place the tiles in each appropriate line (with
+ # those that cannot be placed added to the floor line).
+ for i in range(self.GRID_SIZE):
+ # Can tiles be added to pattern line i?
+ if self.lines_tile[i] != -1 and \
+ self.lines_tile[i] != tile:
+ # these tiles cannot be added to this pattern line
+ continue
+
+ # Is the space on the grid for this tile already
+ # occupied?
+ grid_col = int(self.grid_scheme[i][tile])
+ if self.grid_state[i][grid_col] == 1:
+ # It is, so we cannot place this tile type
+ # in this pattern line!
+ continue
+
+ slots_free = (i + 1) - self.lines_number[i]
+ tg = TileGrab()
+ tg.number = num_avail
+ tg.tile_type = tile
+ tg.pattern_line_dest = i
+ tg.num_to_pattern_line = min(num_avail, slots_free)
+ tg.num_to_floor_line = tg.number - tg.num_to_pattern_line
+
+ moves.append((Move.TAKE_FROM_CENTRE, -1, tg))
+
+ # Default move is to place all the tiles in the floor line
+ tg = TileGrab()
+ tg.number = num_avail
+ tg.tile_type = tile
+ tg.num_to_floor_line = tg.number
+ moves.append((Move.TAKE_FROM_CENTRE, -1, tg))
+
+ return moves
+
+ # Complete scoring process for player at round end:
+ # 2. Move tiles across from pattern lines to the grid and score each;
+ #
+ # 3. Clear remaining tiles on pattern lines (where appropriate) and
+ # return to be added to "used" tiles bag;
+ #
+ # 4. Score penalties for tiles in floor line and return these tiles
+ # to be added to the "used" tiles bag.
+ #
+ # Returns a pair: the change in the player's score; and the set of
+ # tiles to be returned to the "used" tile bag. The players internal
+ # representation of their score is updated in the process.
+ def ScoreRound(self):
+ used_tiles = []
+
+ score_inc = 0
+
+ # 1. Move tiles across from pattern lines to the wall grid
+ for i in range(self.GRID_SIZE):
+ # Is the pattern line full? If not it persists in its current
+ # state into the next round.
+ if self.lines_number[i] == i + 1:
+ tc = self.lines_tile[i]
+ col = int(self.grid_scheme[i][tc])
+
+ # Record that the player has placed a tile of type 'tc'
+ self.number_of[tc] += 1
+
+ # Clear the pattern line, add all but one tile into the
+ # used tiles bag. The last tile will be placed on the
+ # players wall grid.
+ for j in range(i):
+ used_tiles.append(tc)
+
+ self.lines_tile[i] = -1
+ self.lines_number[i] = 0
+
+ # Tile will be placed at position (i,col) in grid
+ self.grid_state[i][col] = 1
+
+ # count the number of tiles in a continguous line
+ # above, below, to the left and right of the placed tile.
+ above = 0
+ for j in range(col - 1, -1, -1):
+ val = self.grid_state[i][j]
+ above += val
+ if val == 0:
+ break
+ below = 0
+ for j in range(col + 1, self.GRID_SIZE, 1):
+ val = self.grid_state[i][j]
+ below += val
+ if val == 0:
+ break
+ left = 0
+ for j in range(i - 1, -1, -1):
+ val = self.grid_state[j][col]
+ left += val
+ if val == 0:
+ break
+ right = 0
+ for j in range(i + 1, self.GRID_SIZE, 1):
+ val = self.grid_state[j][col]
+ right += val
+ if val == 0:
+ break
+
+ # If the tile sits in a contiguous vertical line of
+ # tiles in the grid, it is worth 1*the number of tiles
+ # in this line (including itself).
+ if above > 0 or below > 0:
+ score_inc += (1 + above + below)
+
+ # In addition to the vertical score, the tile is worth
+ # an additional H points where H is the length of the
+ # horizontal contiguous line in which it sits.
+ if left > 0 or right > 0:
+ score_inc += (1 + left + right)
+
+ # If the tile is not next to any already placed tiles
+ # on the grid, it is worth 1 point.
+ if above == 0 and below == 0 and left == 0 \
+ and right == 0:
+ score_inc += 1
+
+ # Update the evaluation criteria
+ self.score_without_bouns += score_inc
+
+ # Score penalties for tiles in floor line
+ penalties = 0
+ for i in range(len(self.floor)):
+ penalties += self.floor[i] * self.FLOOR_SCORES[i]
+ self.floor[i] = 0
+
+ used_tiles.extend(self.floor_tiles)
+ self.floor_tiles = []
+
+ # Update the evaluation criteria
+ self.penalties += penalties
+
+ # Players cannot be assigned a negative score in any round.
+ score_change = score_inc + penalties
+ if score_change < 0 and self.score < -score_change:
+ score_change = -self.score
+
+ self.score += score_change
+
+ self.player_trace.round_scores[-1] = score_change
+
+ return (self.score, used_tiles)
+
+ # Complete additional end of game scoring (add bonuses). Return
+
+ # computed bonus, and add to internal score representation.
+ def EndOfGameScore(self):
+ rows = self.GetCompletedRows()
+ cols = self.GetCompletedColumns()
+ sets = self.GetCompletedSets()
+
+ bonus = (rows * self.ROW_BONUS) + (cols * self.COL_BONUS) + \
+ (sets * self.SET_BONUS)
+
+ # Update the evaluation criteria
+ self.rows_finished = rows
+ self.columns_finished = cols
+ self.sets_finished = sets
+ self.bonus_received = bonus
+
+ self.player_trace.bonuses = bonus
+ self.score += bonus
+
+ return bonus
+
+
+# The GameState class encapsulates the state of the game: the game
+# state for each player; the state of the factory displays and
+# centre tile pool; and the state of the tile bags.
+class GameState:
+ NUM_FACTORIES = [5, 7, 9]
+ NUM_TILE_TYPE = 20
+ NUM_ON_FACTORY = 4
+
+ def __init__(self, num_players):
+ # Create player states
+ self.players = []
+ for i in range(num_players):
+ ps = PlayerState(i)
+ self.players.append(ps)
+
+ # Tile bag contains NUM_TILE_TYPE of each tile colour
+ self.bag = []
+ for i in range(self.NUM_TILE_TYPE):
+ self.bag.append(Tile.BLUE)
+ self.bag.append(Tile.YELLOW)
+ self.bag.append(Tile.RED)
+ self.bag.append(Tile.BLACK)
+ self.bag.append(Tile.WHITE)
+
+ # Shuffle contents of tile bag
+ random.shuffle(self.bag)
+
+ # "Used" bag is initial empty
+ self.bag_used = []
+
+ # In a 2/3/4-player game, 5/7/9 factory displays are used
+ self.factories = []
+ for i in range(self.NUM_FACTORIES[num_players - 2]):
+ td = TileDisplay()
+
+ # Initialise factory display: add NUM_ON_FACTORY randomly
+ # drawn tiles to the factory (if available).
+ self.InitialiseFactory(td)
+ self.factories.append(td)
+
+ self.centre_pool = TileDisplay()
+ self.first_player_taken = False
+ self.first_player = random.randrange(num_players)
+ self.next_first_player = -1
+
+ def TilesRemaining(self):
+ if self.centre_pool.total > 0:
+ return True
+ for fac in self.factories:
+ if fac.total > 0:
+ return True
+ return False
+
+ # Place tiles from the main bag (and used bag if the main bag runs
+ # out of tiles) onto the given factory display.
+ def InitialiseFactory(self, factory):
+ # Reset contents of factory display
+ factory.total = 0
+ for tile in Tile:
+ factory.tiles[tile] = 0
+
+ # If there are < NUM_ON_FACTORY tiles in the bag, shuffle the
+ # tiles in the "used" bag and add them to the main bag (we still
+ # want the tiles that were left in the main bag to be drawn first).
+ # Fill the factory display with tiles, up to capacity, if possible.
+ # If there are less than NUM_ON_FACTORY tiles available in both
+ # bags, the factory will be left at partial capacity.
+ if len(self.bag) < self.NUM_ON_FACTORY and len(self.bag_used) > 0:
+ random.shuffle(self.bag_used)
+ self.bag.extend(self.bag_used)
+ self.bag_used = []
+
+ for i in range(min(self.NUM_ON_FACTORY, len(self.bag))):
+ # take tile out of the bag
+ tile = self.bag.pop(0)
+ factory.tiles[tile] += 1
+ factory.total += 1
+
+ # Setup a new round of play be resetting each of the factory displays
+ # and the centre tile pool
+ def SetupNewRound(self):
+ # Reset contents of each factory display
+ for fd in self.factories:
+ self.InitialiseFactory(fd)
+
+ for tile in Tile:
+ self.centre_pool.tiles[tile] = 0
+
+ self.first_player_taken = False
+ self.first_player = self.next_first_player
+ self.next_first_player = -1
+
+ for plr in self.players:
+ plr.player_trace.StartRound()
+
+ # Execute end of round actions (scoring and clean up)
+ def ExecuteEndOfRound(self):
+ # Each player scores for the round, and we add tiles to the
+ # used bag (if appropriate).
+ for plr in self.players:
+ _, used = plr.ScoreRound()
+ self.bag_used.extend(used)
+
+ # Execute move by given player
+ def ExecuteMove(self, player_id, move):
+ plr_state = self.players[player_id]
+ plr_state.player_trace.moves[-1].append(move)
+
+ # The player is taking tiles from the centre
+ if move[0] == Move.TAKE_FROM_CENTRE:
+ tg = move[2]
+
+ if not self.first_player_taken:
+ plr_state.GiveFirstPlayerToken()
+ self.first_player_taken = True
+ self.next_first_player = player_id
+
+ if tg.num_to_floor_line > 0:
+ ttf = []
+ for i in range(tg.num_to_floor_line):
+ ttf.append(tg.tile_type)
+ plr_state.AddToFloor(ttf)
+ self.bag_used.extend(ttf)
+
+ if tg.num_to_pattern_line > 0:
+ plr_state.AddToPatternLine(tg.pattern_line_dest,
+ tg.num_to_pattern_line, tg.tile_type)
+
+ # Remove tiles from the centre
+ self.centre_pool.RemoveTiles(tg.number, tg.tile_type)
+
+ elif move[0] == Move.TAKE_FROM_FACTORY:
+ tg = move[2]
+ if tg.num_to_floor_line > 0:
+ ttf = []
+ for i in range(tg.num_to_floor_line):
+ ttf.append(tg.tile_type)
+ plr_state.AddToFloor(ttf)
+ self.bag_used.extend(ttf)
+
+ if tg.num_to_pattern_line > 0:
+ plr_state.AddToPatternLine(tg.pattern_line_dest,
+ tg.num_to_pattern_line, tg.tile_type)
+
+ # Remove tiles from the factory display
+ fid = move[1]
+ fac = self.factories[fid]
+ fac.RemoveTiles(tg.number, tg.tile_type)
+
+ # All remaining tiles on the factory display go into the
+ # centre!
+ for tile in Tile:
+ num_on_fd = fac.tiles[tile]
+ if num_on_fd > 0:
+ self.centre_pool.AddTiles(num_on_fd, tile)
+ fac.RemoveTiles(num_on_fd, tile)
+
+ def __str__(self):
+ str1 = ''
+ i = 0
+ for f in self.factories:
+ list = [v for k, v in f.tiles.items()]
+ str1 += 'f' + str(i) + ':' + list.__str__() + '\t'
+ i += 1
+ list = [v for k, v in self.centre_pool.tiles.items()]
+ str1 += 'cp:' + list.__str__() + '\t' + '\n'
+ return str1
+
+ def detail_str(self):
+ str1 = ''
+ i = 0
+ for f in self.factories:
+ list = [v for k, v in f.tiles.items()]
+ str1 += 'f' + str(i) + ':' + list.__str__() + '\t'
+ i += 1
+ list = [v for k, v in self.centre_pool.tiles.items()]
+ str1 += 'cp:' + list.__str__() + '\t' + '\n'
+
+ for i in range(len(self.players)):
+ str1 += PlayerToString(i, self.players[i])
+ return str1
+
+
+# Class representing a policy for playing AZUL.
+class Player(object):
+ def __init__(self, _id):
+ self.id = _id
+ super().__init__()
+
+ # Given a set of available moves for the player to execute, and
+ # a copy of the current game state (including that of the player),
+ # select one of the moves to execute.
+ def SelectMove(self, moves, game_state):
+ return random.choice(moves)
+
+
+# Class that facilities a simulation of a game of AZUL.
+class GameRunner:
+ def __init__(self, player_list, seed):
+ random.seed(seed)
+
+ # Make sure we are forming a valid game, and that player
+ # id's range from 0 to N-1, where N is the number of players.
+ assert (len(player_list) <= 4)
+ assert (len(player_list) > 1)
+
+ i = 0
+ for plyr in player_list:
+ assert (plyr.id == i)
+ i += 1
+
+ self.game_state = GameState(len(player_list))
+ self.players = player_list
+
+ def Run(self, log_state):
+ player_order = []
+ for i in range(self.game_state.first_player, len(self.players)):
+ player_order.append(i)
+
+ for i in range(0, self.game_state.first_player):
+ player_order.append(i)
+
+ game_continuing = True
+ for plr in self.game_state.players:
+ plr.player_trace.StartRound()
+
+ while game_continuing:
+ for i in player_order:
+ plr_state = self.game_state.players[i]
+ moves = plr_state.GetAvailableMoves(self.game_state)
+
+ gs_copy = copy.deepcopy(self.game_state)
+ moves_copy = copy.deepcopy(moves)
+ selected = self.players[i].SelectMove(moves_copy, gs_copy)
+ #print(gs_copy)
+ #print(selected[1],selected[2])
+ assert (ValidMove(selected, moves))
+
+ if log_state:
+ print("\nPlayer {} has chosen the following move:".format(
+ i))
+ print(MoveToString(i, selected))
+ print("\n")
+
+ self.game_state.ExecuteMove(i, selected)
+
+ if log_state:
+ print("The new player state is:")
+ print(PlayerToString(i, plr_state))
+
+ if not self.game_state.TilesRemaining():
+ break
+
+ # Have we reached the end of round?
+ if self.game_state.TilesRemaining():
+ continue
+
+ # It is the end of round
+ if log_state:
+ print("ROUND HAS ENDED")
+
+ self.game_state.ExecuteEndOfRound()
+
+ # Is it the end of the game?
+ for i in player_order:
+ plr_state = self.game_state.players[i]
+ completed_rows = plr_state.GetCompletedRows()
+
+ if completed_rows > 0:
+ game_continuing = False
+ break
+
+ # Set up the next round
+ if game_continuing:
+ self.game_state.SetupNewRound()
+ player_order = []
+ for i in range(self.game_state.first_player, len(self.players)):
+ player_order.append(i)
+
+ for i in range(0, self.game_state.first_player):
+ player_order.append(i)
+
+ scores = [self.game_state.players[0].score,
+ self.game_state.players[1].score]
+ set_round_recorder(scores)
+ print(get_round_recorder())
+ print(get_pre_r())
+
+ if log_state:
+ print("THE GAME HAS ENDED")
+
+ # Score player bonuses
+ player_traces = {}
+ for i in player_order:
+ plr_state = self.game_state.players[i]
+ plr_state.EndOfGameScore()
+
+ # Update the evaluation result
+ player_traces[i] = (plr_state.score, plr_state.player_trace,
+ plr_state.score_without_bouns, plr_state.rows_finished,
+ plr_state.columns_finished, plr_state.sets_finished,
+ plr_state.penalties, plr_state.bonus_received)
+
+ scores = [self.game_state.players[0].score,
+ self.game_state.players[1].score]
+ set_round_recorder(scores)
+ print(get_round_recorder())
+ print(get_pre_r())
+
+ # Return scores
+ return player_traces
--
GitLab