Pale Machine CSE101
Objectives
What we will evaluate today: Everything!
Setup: Before you start
Launch Spyder (from the Applications/Programming menu). If you see a “Spyder update” message, just click OK and ignore it.
Before you start: create a new
project, called PracticalExam_2
(using “New
Project” from the “Projects” menu). This will ensure that files from
last week (and future weeks) do not get mixed up together.
Now download the following files by right-clicking
them, choosing Save as, and saving them to your
PracticalExam_2
folder.
Check and make sure that they have all appeared in
your PracticalExam_2
project in Spyder.
Now create a new file named
scotlandyard.py
. This is the file in which you will be
writing your code today.
Introduction
In the series of old board games that bring tears to the eyes of CSE101 teachers is ‘Scotland Yard’, played by two or more players. The game is named after the headquarters of London’s Metropolitan Police Service.
In this game, all players except one play together as a team of detectives. The detectives search a criminal – played by the remaining player – who tries to hide. The detectives as well as the criminal use public transport in order to move through the city, however, due to financial reasons, each detective only has a limited amount of money to spend on tickets. The criminal is moving secretly, however, his position is reveiled from time to time such that the detectives can correct their course. As soon as one of the detectives reaches the same position as the criminal, the detectives win the game. If the detectives spent all their money or cannot move any further but haven’t found the criminal, the criminal wins the game.
We will implement a simplified version of this game today and imagine playing on a grid of squares. Hence, we can move to adjacent squares (horizontal or vertical) by bus or we can do longer moves by metro from one station to the next. The bus tickets cost 1 coin and the metro tickets 2 coins.
Exercises
Exercise 1: Players
We first create a new class Player
to
represent game players. Players are defined by their name, their
position on the board, and their role, either detective or criminal.
Additionally, they own a number of coins to be spent on public
transportation tickets.
Your class Player
should begin as follows:
class Player:
"""
Encapsulates players and their properties
Data Attributes:
name -- the name of the player
position -- the current position of the player on the board
role -- the role of the player, detective or criminal
coins -- number of coins for tickets
"""
def __init__(self, name, posx, posy, role, coins):
pass # remove this line and replace with your own code
def __str__(self):
pass # remove this line and replace with your own code
Complete the two methods __init__
and
__str__
.
Testing
Test your class Player
in the console,
for example as follows:
In [1]: p1 = Player('Benjamin', 2, 4, 'detective', 4)
In [2]: print(p1)
Player Benjamin is detective in position (2,4) with 4 coins
In [3]: p1.position = (4,3)
In [4]: print(p1)
Player Benjamin is detective in position (4,3) with 4 coins
In [5]: p1.coins = 0
In [6]: print(p1)
Player Benjamin is detective in position (4,3) with 0 coins
In [7]: p2 = Player('Maxi', 5, 5, 'criminal', 3)
In [8]: print(p2)
Player Maxi is criminal in position (5,5) with 3 coins
You do not need to adapt the word coins, it is ok to write 1 coins.
Exercise 2: Player Moves
Now complete the class Player
with
three other methods: 1. the method bus_move
checks if the
given position is adjacent to the player’s current position and if yes,
returns True
. Otherwise, it returns False
. 2.
the method can_move
tests if the player can move by bus or
metro based on the number of coins. For a bus move, 1
coin is needed and taking a metro costs 2
coins. 3. the method move
will move the player to
the given next position. It updates the position of the player, the
number of coins and prints a message as given in the examples. We assume
that move
is only called after can_move
returned True.
def bus_move(self, nextx, nexty):
"""
Checks if (nextx,nexty) is adjacent to the current position and if yes, returns True.
Otherwise, return False
"""
pass # remove this line and replace with your own code
def can_move(self, nextx, nexty):
"""
Tests if a player can move to the given position based on the number of coins
"""
pass # remove this line and replace with your own code
def move(self, nextx, nexty):
"""
The method moves the player to another position by updating the position and the number
of coins, and printing a message about the action.
We assume that move will only be called after can_move evaluated to True.
"""
pass # remove this line and replace with your own code
Testing
Test your class Player
in the console,
for example as follows:
In [9]: p1 = Player('Benjamin', 4, 3, 'detective', 4)
In [10]: print(p1)
Player Benjamin is a detective in position (4,3) with 4 coins
In [11]: p1.move(5, 3)
Player Benjamin took the bus to position (5,3)
Out[11]: (5,3)
In [12]: p1.coins
Out[12]: 3
In [13]: p1.move(1, 1)
Player Benjamin took the metro to position (1,1)
Out[13]: (1,1)
In [14]: p2 = Player('Maxi', 5, 5, 'criminal', 6)
In [15]: print(p2)
Player Maxi is criminal in position (5,5) with 6 coins
In [16]: p2.move(2, 2)
Player Maxi took the metro to a secret position
Out[16]: (2,2)
Note: It is important to test your function in the console as there is no grader feedback.
Upload
Now take a moment to upload your scotlandyard.py
file to
the
moodle assignment corresponding to this exam.
(You can upload scotlandyard.py
several times, but
only your final upload will be graded - don’t wait until the last moment
to upload a preliminary version, in case you miss the cut-off!)
Exercise 3: Metro station
We now implement the metro stations as instances of the class
MetroStation
. A metro station is connected to other metro
stations on the board by a metro line. To define a metro station, we
need to know its position on the board. The second class attribute is a
set of metro stations to go to.
Our class MetroStation
will therefore work as follows:
each object has two data attributes, position
and
next_stations
, being a set of positions on the board with
connected metro stations.
Here is some code to get you started:
class MetroStation:
"""
Data Attributes:
position -- own position of the station
next_stations -- a set of coordinates for the subsequent stations on the line
"""
def __init__(self, x, y):
pass # remove this line and replace with your own code
def __str__(self):
pass # remove this line and replace with your own code
Hint: In an f-string you can double the curly braces
{{}}
to prevent them from being interpreted as part of the
expression.
Add a method to the class MetroStation
called add_next_station(self, position)
that adds the
coordinates of a connected metro stations to the set of neighboring
stations
def add_next_station(self, position):
pass # remove this line and replace with your own code
Testing
You should be able to reproduce the following behavior:
In [17]: s = MetroStation(2,3)
In [18]: print(s)
Metro station at position (2,3) with next stations {}
In [19]: s.add_next_station((5,4))
In [20]: s.next_stations
Out[20]: {(5,4)}
In [21]: print(s)
Metro station at position (2,3) with next stations {(5,4)}
In [22]: s.add_next_station((7,3))
In [23]: s.next_stations
Out[23]: {(5,4),(7,3)}
In [24]: print(s)
Metro station at position (2,3) with next stations {(5,4),(7,3)}
Upload
Now take a moment to upload your scotlandyard.py
file to
the
moodle assignment corresponding to this exam.
(Remember, you can upload scotlandyard.py
several
times, but only your final upload will be graded - don’t wait until the
last moment to upload a preliminary version, in case you miss the
cut-off!)
Exercise 4: The board
Objects from the class Board
for the Scotland Yard game
have four data attributes:
xsize
which is the number of squares in x direction of the board,ysize
which is the number of squares in y direction of the board,- a dictionary
metro_stations
, whose keys are tuples of coordinates on the board, with the corresponding values beingMetroStation
objects at those positions, and - a two-dimensional list representing our
gameboard
. Here ‘-’ are empty squares, ‘M’ shows metro stations, ‘P’ the position of detectives and ‘X’ the position of the criminal.
Note: The coordinates of the board are always between 1 and xszie or ysize!
Copy the following code.
class Board:
"""
A gameboard for our version of Scotland Yard
Data Attributes:
xsize -- the number of squares in x direction
ysize -- the number of squares in y direction
metro_stations -- a dictionary with the metro stations on the board
gameboard -- a two-dimensional list as the map of the board
"""
def __init__(self, xsize, ysize):
self.xsize = xsize
self.ysize = ysize
self.metro_stations = dict()
self.create_gameboard()
def __str__(self):
= f'Board of size {self.xsize}x{self.ysize}'
outstr for p,m in self.metro_stations.items():
+= '\n'
outstr += f'{m}'
outstr return outstr
def create_gameboard(self):
"""
Method to represent the board in its current state
"""
self.gameboard = [['-'] * (self.ysize+1) for i in range(self.xsize+1)]
for j in range(len(self.gameboard[0])):
self.gameboard[0][j] = ""
for i in range(len(self.gameboard)):
self.gameboard[i][0] = ""
def draw_board(self):
"""
Method to draw the board in the console
"""
= f'Board of size {self.xsize}x{self.ysize}'
outstr for i in range(len(self.gameboard)):
+="\n"
outstrfor j in range(len(self.gameboard[i])):
+=(self.gameboard[i][j])
outstrprint(outstr)
Testing
In [25]: b = Board(8,8)
In [26]: b.xsize
Out[26]: 8
In [27]: print(b)
Board of size 8x8
In [28]: m1 = MetroStation(2,2)
In [29]: b.metro_stations[(2,2)] = m1
In [30]: print(b)
Board of size 8x8
Metro station at position (2, 2) with next stations {}
In the sequel it will be convenient to have methods dealing with the metro stations on the board.
Copy the following code into the class
Board
and complete the three methods.
- The method
has_metro_station
returns True, if there is a metro station on this position of the board, False otherwise. - The method
create_metro_station
will create a new MetroStation, add it to the board and return the metro station object. We assumecreate_metro_station
will be called only with valid arguments: the integersx
andy
are between 1 and the size of the board (inclusive). - The method
get_distance
returns the distance on the board between two positions of the board being the sum of the squared distances of x and y coordinates. The Euclidian distance of two positions (x1,y1) and (x2,y2): (x1-x2)2+(y1-y2)2.
def has_metro_station(self, pos):
"""True if and only if pos has a metro station on the board."""
pass # remove this line and replace with your own code
def create_metro_station(self, x, y):
"""Set metro station at (x,y) and return the metro station object"""
pass # remove this line and replace with your own code
def get_distance(self, pos1, pos2):
"""Calculate (x1-x2)^2+(y1-y2)^2 and return it"""
pass # remove this line and replace with your own code
Testing
In [31]: b = Board(8,8)
In [32]: b.has_metro_station((4,3))
Out[32]: False
In [33]: b.draw_board()
Board of size 8x8
--------
--------
--------
--------
--------
--------
--------
--------
In [34]: b.get_distance((2,2),(4,4))
Out[34]: 8
In [35]: m1 = b.create_metro_station(1,1)
In [36]: print(m1)
Metro station at position (1, 1) with next stations {}
In [37]: m2 = b.create_metro_station(8,8)
In [38]: b.draw_board()
Board of size 8x8
M-------
--------
--------
--------
--------
--------
--------
-------M
In [39]: b.has_metro_station((8,8))
Out[39]: True
We allow printing the metro stations in any order, so the output does not have to have exactly the same order.
Exercise 5: The next available positions
Copy the following code into the class
Board
and complete the method.
The method get_next_pos
returns a list of possible
positions the player can move to by bus or metro, if there is a station.
The player cannot move outside of the board, hence the coordinates
cannot go below 1 or above xsize
or ysize
. The
player can move up, down, left or right by bus or to connected metro
stations if the square has a metro station (use
has_metro_station
). For each position, it can be checked if
the player can move with the method can_move
. If the player
can’t move, return an empty list. Use the method is_free
to
check if the possible next position is still free.
def is_free(self, pos):
"""
This method uses the board_game in order to check if a square is already occupied by a
detective
"""
= pos[0]
x = pos[1]
y return (self.gameboard[x][y]!='P')
def clean_position(self, pos):
"""
Remove player symbol P from board and redraw M or -
"""
= pos[0]
x = pos[1]
y if(self.has_metro_station(pos)):
self.gameboard[x][y]='M'
else:
self.gameboard[x][y]='-'
def draw_player(self, player):
"""
Gets a player object and draws it on the gamebord
"""
= player.position[0]
x = player.position[1]
y if(player.role == "detective"):
self.gameboard[x][y]='P'
if(player.role == "criminal"):
self.gameboard[x][y]='X'
def add_metro_lines(self,pos):
"""
Creates the connections between metro stations.
Calculate the 2 nearest neighboring metro stations on the board
and add them to next_stations.
The input argument is the position of the current metro station which has been created before
using create_metro_station.
"""
= list(self.metro_stations.keys())
mpos
mpos.remove(pos)"""The following sort function sorts the metro stations by distance from the current one"""
= lambda x : self.get_distance(x, pos))
mpos.sort(key if(len(mpos)>0):
self.metro_stations[pos].add_next_station(mpos[0])
if(len(mpos)>1):
self.metro_stations[pos].add_next_station(mpos[1])
def get_next_pos(self, player_pos):
"""
Get next position a player can move to.
Use the method is_free() to check if the possible next position is still free
"""
pass # remove this line and replace with your own code
Test your methods in the console, for example as follows.
In [40]: b = Board(8,8)
In [41]: b.get_next_pos((2,2))
Out[41]: [(1,2), (2,1), (2,3), (3,2)]
In [42]: m1 = b.create_metro_station(1,2)
In [43]: m2 = b.create_metro_station(7,1)
In [44]: m3 = b.create_metro_station(2,8)
In [45]: m4 = b.create_metro_station(8,8)
In [46]: b.draw_board()
Board of size 8x8
-M------
-------M
--------
--------
--------
--------
M-------
-------M
In [47]: print(b)
Board of size 8x8
Metro station at position (1, 2) with next stations {}
Metro station at position (7, 1) with next stations {}
Metro station at position (2, 8) with next stations {}
Metro station at position (8, 8) with next stations {}
In [48]: b.add_metro_lines((1, 2))
In [49]: print(b)
Board of size 8x8
Metro station at position (1, 2) with next stations {(7, 1), (2, 8)}
Metro station at position (7, 1) with next stations {}
Metro station at position (2, 8) with next stations {}
Metro station at position (8, 8) with next stations {}
In [50]: b.add_metro_lines((7, 1))
In [51]: b.add_metro_lines((2, 8))
In [52]: b.add_metro_lines((8, 8))
In [53]: print(b)
Board of size 8x8
Metro station at position (1, 2) with next stations {(7, 1), (2, 8)}
Metro station at position (7, 1) with next stations {(8, 8), (1, 2)}
Metro station at position (2, 8) with next stations {(8, 8), (1, 2)}
Metro station at position (8, 8) with next stations {(7, 1), (2, 8)}
In [54]: b.get_next_pos((1,2))
Out[54]: [(2, 2), (1, 3), (1, 1), (7, 1), (2,8)]
In [55]: b.is_free((2,3))
Out[55]: True
We allow printing the next positions and the metro stations in any order, so the output does not have to have exactly the same order.
Upload
Now take a moment to upload your scotlandyard.py
file to
the
moodle assignment corresponding to this exam.
(Remember, you can upload scotlandyard.py
several
times, but only your final upload will be graded - don’t wait until the
last moment to upload a preliminary version, in case you miss the
cut-off!)
Exercise 6: The Game
We have now everything in hand to define the main class
Game
: to define a game, we need a board, a list of players’
names whereas we choose the last player to be the
criminal.
Game
objects have four data attributes:
- an integer
turn
which contains the current turn, board
which contains aBoard
object,players
, a list ofPlayer
objects.- a position
last_x_pos
storing the position where the criminal has been seen last
Copy and add the class Game
with the
methods given below to your code.
Add import random
to the beginning of
your scotlandyard.py file, so that you can use the randint
method of the random
module.
Complete the __init__(board, players)
method. Follow the steps when creating the list of
Player objects: 1. The positions of the players should be chosen
randomly, however, at most one player can be placed on each position of
the board, use the method is_free()
. Thus, get a random
position for each player 2. The number of coins depends on the board
size and the number of players, hence: coins =
2(board.sizex+board.sizey)/number_players. The criminal (the last player
in the list) is very smart and owns double the amount of
coins than each of the detectives. Use integer division as half
a coin doesn’t exist in our game. 3. Call the function
draw_player()
of your board in order to add your players to
the gameboard representation. 4. Initialize last_x_pos
to
the start position of the criminal
class Game:
"""
A game of Scotland Yard.
Data Attributes:
board -- the board of the game
players -- a list of Player objects with the last one playing Mr. X
turn -- the number of the current turn
last_x_pos -- the position the criminal has been seen last
"""
def __init__(self, board, player_names):
self.turn = 0
self.board = board
pass # remove this line and replace with your own code
def print_game_state(self):
"""Print state of game after every turn."""
print('-------------game state-------------')
for player in self.players:
print(player)
print('-------------game state-------------\n')
Test your method __init__
in the
console, for example as follows.
In [56]: b = Board(8,8)
In [57]: g = Game(b, ['Benjamin', 'Noemie', 'Maxi'])
In [58]: g.print_game_state()
-------------game state-------------
Player Benjamin is detective in position (1, 2) with 10 coins
Player Noemie is detective in position (1, 5) with 10 coins
Player Maxi is criminal in position (5, 5) with 20 coins
-------------game state-------------
In [59]: g.board.draw_board()
Board of size 8x8
-P--P---
--------
--------
--------
----X---
--------
--------
--------
As the players are placed on random positions, your gameboard may look different.
Upload
Now take a moment to upload your scotlandyard.py
file to
the
moodle assignment corresponding to this exam.
(Remember, you can upload scotlandyard.py
several
times, but only your final upload will be graded - don’t wait until the
last moment to upload a preliminary version, in case you miss the
cut-off!)
Exercise 7: Reading from a file
To test your function, it will be convenient to use pre-defined
boards and player lists. The three files (board1.txt
,
board2.txt
and board3.txt
) given at the top of
this page contain information to define a game.
- The first line contains the size of the board separated by white space
- The second line contains the names of the players, separated by a white space.
- The remaining lines contain the position of a metro station, one station per line
Your function should return a Game
, hence you first have
to create a Board
including its metro stations. Then you
can create a Game
object with the board and the list of
player names as arguments.
We strongly encourage you to open the files board1.txt
,
board2.txt
and board3.txt
to see how they are
made.
Don’t forget to call add_metro_lines
for each metro station in order to connect the stations. This should be
done after all the metro stations have been
created.
Write the function
load_from_file(filename)
(in scotlandyard.py,
outside the Player
, MetroStation
,
Game
and Board
classes), and test it, for
example as follows:
In [59]: g = load_from_file('board1.txt')
In [60]: g.board.draw_board()
Board of size 8x8
P------P
---X----
------P-
--------
--------
--------
--------
M------M
In [60]: g.print_game_state()
-------------game state-------------
Player Andi is detective in position (1, 8) with 8 coins
Player Maxi is detective in position (3, 7) with 8 coins
Player Kim is detective in position (1, 1) with 8 coins
Player Alex is criminal in position (2, 4) with 16 coins
-------------game state-------------
In [60]: print(g.board)
Board of size 8x8
Metro station at position (1, 1) with next stations {(1, 1), (1, 8)}
Metro station at position (1, 8) with next stations {(1, 1), (1, 8)}
Metro station at position (8, 8) with next stations {(8, 8), (1, 8)}
Metro station at position (8, 1) with next stations {(1, 1), (8, 1)}
As the players are set in random positions, your gameboard may look different. If a player is on a metro station, you only see the ‘P’ and the ‘M’ is not shown.
Hint: recall that the split
method accepts a
separator as an argument, and this separator may be ':'
or
' '
.
Upload
Now take a moment to upload your scotlandyard.py
file to
the
moodle assignment corresponding to this exam.
(Remember, you can upload scotlandyard.py
several
times, but only your final upload will be graded - don’t wait until the
last moment to upload a preliminary version, in case you miss the
cut-off!)
Exercise 8: Prepare to play
We detail a bit more the rules of the game. At each turn of the game, the players move in the given order by the list of players.
In each turn:
- we update the attribute
turn
bychange_turn
- for each player in the list, it calls
move_player
- if none of the detectives changed position, the criminal won
- if a detective moved to the criminal’s position, the detectives won
To win, a player must catch the criminal by moving on the same position. If none of the detectives can take a further move, the criminal has won the game. We defined several methods that will help for the next exercise.
Copy this code inside the class Game
,
and complete the methods.
- The method
change_turn
will update the turn by 1 and every 5th turn (turn % 5 == 0) it will update the position of the criminal (last_x_pos
) as well as the drawing on the board (useclean_position
anddraw_player
) - The method
move_player
:- first, it stores the current position of the player, as we might have to return it (see below)
- calls
get_next_pos
to get possible next positions - filter the positions in case no bustickets or metrotickets are left
- if no possible next positions are left, return the original position as the player cannot move
- for all possible next positions,
- if the player is detective:take the one with the minimal distance to
last_x_pos
- if the player is criminal: take a random position using random.choice
- if the player is detective:take the one with the minimal distance to
- update the player’s position (using the function
move
in the player class), clean the previous position from the board (clean_position
), redraw the player (draw_player
) and return the player’s new position
def change_turn(self):
pass # remove this line and replace with your own code
def move_player(self, curr_player):
pass # remove this line and replace with your own code
Testing
Test your methods change_turn
, and
move_player
in the console; for example, you
should have the following behavior:
In [61]: g = load_from_file('board1.txt')
In [62]: g.print_game_state()
------------game state-------------
Player Andi is detective in position (5, 3) with 8 coins
Player Maxi is detective in position (2, 8) with 8 coins
Player Kim is detective in position (3, 1) with 8 coins
Player Alex is criminal in position (8, 4) with 16 coins
-------------game state-------------
In [63]: g.board.draw_board()
Board of size 8x8
M------M
-------P
P-------
--------
--P-----
--------
--------
M--X---M
In [64]: g.turn
Out[64]: 0
In [65]: g.change_turn()
In [66]: g.turn
Out[66]: 1
In [67]: g.last_x_pos
Out[67]: (8, 4)
In [68]: g.move_player(g.players[0])
Player Andi took the bus to position (6, 3)
Out[68]: (6, 3)
In [69]: g.move_player(g.players[1])
Player Maxi took the bus to position (3, 8)
Out[69]: (3, 8)
In [70]: g.move_player(g.players[2])
Player Kim took the bus to position (4, 1)
Out[70]: (4, 1)
In [71]: g.move_player(g.players[3])
Player Alex took the bus to a secret position
Out[71]: (8, 3)
In [72]: g.board.draw_board()
Board of size 8x8
M------M
--------
-------P
P-------
--------
--P-----
--------
M-X----M
In [73]: g.print_game_state()
-------------game state-------------
Player Andi is detective in position (6, 3) with 7 coins
Player Maxi is detective in position (3, 8) with 7 coins
Player Kim is detective in position (4, 1) with 7 coins
Player Alex is criminal in position (8, 3) with 15 coins
-------------game state-------------
Upload
Now take a moment to upload your scotlandyard.py
file to
the
moodle assignment corresponding to this exam.
(Remember, you can upload scotlandyard.py
several
times, but only your final upload will be graded - don’t wait until the
last moment to upload a preliminary version, in case you miss the
cut-off!)
Exercise 9: Combining everything: let’s play!
We now have everything in place to create the main method in the
class Game
. Add some print statements after the game is
over as in the example. It is also helful to print the number of the
turn
and draw_board
to see the progress of the
game. The function play
returns 0 if the criminal won and 1
if the detectives won the game.
Copy and complete the following code in your class
Game
:
def play(self):
"""
Starting point the of game, which continues while players can move or the criminal is still free:
- draw board to see the progress of the game
- change_turn, print the turn after each change
- move players one by one
- check if a player caught the criminal
- return 1 if the detectives won and 0 if the criminal won
"""
pass # remove this line and replace with your own code
Testing
Test your method play()
in the console;
for example, you should have the following behavior with
board1.txt
, board2.txt
and
board3.txt
(though given that we are using the random
module, you may get different values and final result):
Do not forget that the position of the criminal in
shown by draw_board
will only be updated every 5th turn, as
well as last_x_pos
. However print_game_state
always knows the exaxt position of all players.
In [74]: g = load_from_file('board1.txt')
In [75]: g.play()
Board of size 8x8
M-P----M
--------
-----P--
--------
-------P
X-------
--------
M------M
Turn: 1
Player Andi took the bus to position (3, 5)
Player Maxi took the bus to position (5, 7)
Player Kim took the bus to position (2, 3)
Player Alex took the bus to a secret position
Board of size 8x8
M------M
--P-----
----P---
--------
------P-
X-------
--------
M------M
Turn: 2
Player Andi took the bus to position (3, 4)
Player Maxi took the bus to position (5, 6)
Player Kim took the bus to position (3, 3)
Player Alex took the bus to a secret position
Board of size 8x8
M------M
--------
--PP----
--------
-----P--
X-------
--------
M------M
Turn: 3
Player Andi took the bus to position (4, 4)
Player Maxi took the bus to position (5, 5)
Player Kim took the bus to position (4, 3)
Player Alex took the bus to a secret position
Board of size 8x8
M------M
--------
--------
--PP----
----P---
X-------
--------
M------M
Turn: 4
Player Andi took the bus to position (5, 4)
Player Maxi took the bus to position (6, 5)
Player Kim took the bus to position (5, 3)
Player Alex took the bus to a secret position
Board of size 8x8
M------M
--------
--------
--------
--PP----
X---P---
--------
M------M
Turn: 5
Player Andi took the bus to position (6, 4)
Player Maxi took the bus to position (7, 5)
Player Kim took the bus to position (6, 3)
Player Alex took the bus to a secret position
Board of size 8x8
M------M
--------
--------
--------
--------
--PP----
-X--P---
M------M
Turn: 6
Player Andi took the bus to position (7, 4)
Player Maxi took the bus to position (6, 5)
Player Kim took the bus to position (7, 3)
Detective Kim caught the criminal in position (7, 3)
Board of size 8x8
M------M
--------
--------
--------
--------
----P---
-XPP----
M------M
Out[75]: 1
In [76]: g = load_from_file('board2.txt')
In [77]: g.play()
Board of size 5x5
PXM--
----P
P---M
-----
--M-P
Turn: 1
Player Mum took the bus to position (4, 5)
Player Dad took the metro to position (1, 3)
Player Grandpa took the bus to position (1, 2)
Detective Grandpa caught the criminal in position (1, 2)
Board of size 5x5
-PP--
----P
M---M
----P
--M--
Out[77]: 1
In [78]: g = load_from_file('board2.txt')
In [79]: g.play()
Board of size 5x5
--PP-
P----
M-X-M
----P
--M--
Turn: 1
Player Mum took the bus to position (2, 2)
Player Dad took the bus to position (2, 4)
Player Grandpa took the bus to position (4, 4)
Player Grandma took the bus to position (2, 3)
Player Me took the bus to a secret position
Board of size 5x5
--M--
-PPP-
M-X-M
---P-
--M--
Turn: 2
Player Mum took the bus to position (3, 2)
Detective Mum caught the criminal in position (3, 2)
Board of size 5x5
--M--
--PP-
MPX-M
---P-
--M--
Out[79]: 1
In [80]: g = load_from_file('board3.txt')
In [81]: g.play()
Board of size 3x3
X-P
--P
--M
Turn: 1
Player Mia took the bus to position (2, 2)
Player Oscar took the bus to position (1, 2)
Player Ali took the metro to a secret position
Board of size 3x3
XP-
-P-
--M
Turn: 2
Player Mia took the bus to position (2, 1)
Player Oscar took the bus to position (1, 1)
Player Ali took the bus to a secret position
Board of size 3x3
P--
P--
--M
Turn: 3
Player Mia took the bus to position (2, 2)
Player Oscar took the bus to position (2, 1)
Player Ali took the bus to a secret position
Board of size 3x3
M--
PP-
--M
Turn: 4
Player Mia took the bus to position (1, 2)
Player Oscar took the bus to position (1, 1)
Player Ali took the bus to a secret position
Board of size 3x3
PP-
---
--M
Turn: 5
Player Ali took the bus to a secret position
Detectives cannot move, criminal won!
Board of size 3x3
MP-
---
-XM
Out[81]: 0
Upload
Now take a moment to upload your scotlandyard.py
file to
the
moodle assignment corresponding to this exam.
(Remember, you can upload scotlandyard.py
several
times, but only your final upload will be graded - don’t wait until the
last moment to upload a preliminary version, in case you miss the
cut-off!)