-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy paththree_musketeers.py
368 lines (326 loc) · 13.2 KB
/
three_musketeers.py
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# The Three Musketeers Game
# In all methods,
# A 'location' is a two-tuple of integers, each in the range 0 to 4.
# The first integer is the row number, the second is the column number.
# A 'direction' is one of the strings "up", "down", "left", or "right".
# A 'board' is a list of 5 lists, each containing 5 strings: "M", "R", or "-".
# "M" = Musketeer, "R" = Cardinal Richleau's man, "-" = empty.
# Each list of 5 strings is a "row"
# A 'player' is one of the strings "M" or "R" (or sometimes "-").
#
# For brevity, Cardinal Richleau's men are referred to as "enemy".
# 'pass' is a no-nothing Python statement. Replace it with actual code.
def create_board():
global board
"""Creates the initial Three Musketeers board and makes it globally
available (That is, it doesn't have to be passed around as a
parameter.) 'M' represents a Musketeer, 'R' represents one of
Cardinal Richleau's men, and '-' denotes an empty space."""
m = 'M'
r = 'R'
board = [ [r, r, r, r, m],
[r, r, r, r, r],
[r, r, m, r, r],
[r, r, r, r, r],
[m, r, r, r, r] ]
def set_board(new_board):
"""Replaces the global board with new_board."""
global board
board = new_board
def get_board():
"""Just returns the board. Possibly useful for unit tests."""
return board
def string_to_location(s):
"""Given a two-character string (such as 'A5'), returns the designated
location as a 2-tuple (such as (0, 4)).
The function should raise ValueError exception if the input
is outside of the correct range (between 'A' and 'E' for s[0] and
between '1' and '5' for s[1])
"""
# Replace with code
s_letter = {'A':0,'B':1,'C':2,'D':3,'E':4}
s_number = {'1':0,'2':1,'3':2,'4':3,'5':4}
try:
s_location = (s_letter[s[0]],s_number[s[1]])
return s_location
except:
raise ValueError('the input is outside of the correct range')
def location_to_string(location):
"""Returns the string representation of a location.
Similarly to the previous function, this function should raise
ValueError exception if the input is outside of the correct range
"""
# Replace with code
location_num1 = {0:'A',1:'B',2:'C',3:'D',4:'E'}
location_num2 = {0:'1',1:'2',2:'3',3:'4',4:'5'}
try:
return (location_num1[location[0]]+location_num2[location[1]])
except:
raise ValueError('the input is outside of the correct range')
def at(location):
"""Returns the contents of the board at the given location.
You can assume that input will always be in correct range."""
try:
return board[location[0]][location[1]]
except:
raise ValueError('the input is outside of the correct range')
def all_locations():
"""Returns a list of all 25 locations on the board."""
# Replace with code
allocations = []
for i in range (0,5):
for j in range (0,5):
allocations.append((i,j))
return allocations
def adjacent_location(location, direction):
"""Return the location next to the given one, in the given direction.
Does not check if the location returned is legal on a 5x5 board.
You can assume that input will always be in correct range."""
(row, column) = location
# Replace with code
if direction == 'left':
column -= 1
elif direction =='right':
column += 1
elif direction == 'up':
row -= 1
elif direction == 'down':
row += 1
return (row, column)
def is_legal_move_by_musketeer(location, direction):
"""Tests if the Musketeer at the location can move in the direction.
You can assume that input will always be in correct range. Raises
ValueError exception if at(location) is not 'M'"""
# Replace with code
if at(location)!='M':
raise ValueError('No Musketeer at ',location)
else:
return (is_within_board(location, direction) and
at(adjacent_location(location, direction)) == 'R')
def is_legal_move_by_enemy(location, direction):
"""Tests if the enemy at the location can move in the direction.
You can assume that input will always be in correct range. Raises
ValueError exception if at(location) is not 'R'"""
# Replace with code
if at(location)!='R':
raise ValueError("No Cardinal Richleau's man at ",location)
else:
return (is_within_board(location, direction) and
at(adjacent_location(location, direction)) == '-')
def is_legal_move(location, direction):
"""Tests whether it is legal to move the piece at the location
in the given direction.
You can assume that input will always be in correct range."""
# Replace with code
if at(location) == 'M':
return is_legal_move_by_musketeer(location, direction)
elif at(location) == 'R':
return is_legal_move_by_enemy(location, direction)
else:
raise ValueError('No player at ',location)
def can_move_piece_at(location):
"""Tests whether the player at the location has at least one move available.
You can assume that input will always be in correct range.
You can assume that input will always be in correct range."""
# Replace with code
k = 0
for direction in ("up","down","left","right"):
if is_legal_move(location, direction) == True:
k+=1
if k>0: return True
else: return False
def has_some_legal_move_somewhere(who):
"""Tests whether a legal move exists for player "who" (which must
be either 'M' or 'R'). Does not provide any information on where
the legal move is.
You can assume that input will always be in correct range."""
# Replace with code
k = 0
for row in range(0,5):
for column in range (0,5):
if at((row,column)) == who:
if can_move_piece_at((row,column)) == True:
k+=1
break
if k>0: return True
else: return False
def possible_moves_from(location):
"""Returns a list of directions ('left', etc.) in which it is legal
for the player at location to move. If there is no player at
location, returns the empty list, [].
You can assume that input will always be in correct range."""
# Replace with code
possible_moves = []
if at(location) == '-':
return possible_moves
else:
for direction in ('up','down','left','right'):
if is_legal_move(location, direction) == True:
possible_moves += [direction]
return possible_moves
def is_legal_location(location):
"""Tests if the location is legal on a 5x5 board.
You can assume that input will always be a pair of integers."""
# Replace with code
return location in all_locations()
def is_within_board(location, direction):
"""Tests if the move stays within the boundaries of the board.
You can assume that input will always be in correct range."""
# Replace with code
(row,column) = adjacent_location(location, direction)
if (row in range (0,5)) and (column in range (0,5)):
return True
else:
return False
def all_possible_moves_for(player):
"""Returns every possible move for the player ('M' or 'R') as a list
(location, direction) tuples.
You can assume that input will always be in correct range."""
# Replace with code
all_possible_moves = []
for location in all_locations():
if at(location) == player:
for move in possible_moves_from(location):
all_possible_moves += [(location,move)]
return all_possible_moves
def make_move(location, direction):
"""Moves the piece in location in the indicated direction.
Doesn't check if the move is legal. You can assume that input will always
be in correct range."""
player = at(location)
new_location = adjacent_location(location, direction)
board[new_location[0]][new_location[1]] = player
board[location[0]][location[1]] = '-'
def choose_computer_move(who):
"""The computer chooses a move for a Musketeer (who = 'M') or an
enemy (who = 'R') and returns it as the tuple (location, direction),
where a location is a (row, column) tuple as usual.
You can assume that input will always be in correct range."""
# Replace with code
all_moves = []
for location in all_locations():
if at(location) == who:
for move in possible_moves_from(location):
all_moves += [(location, move)]
return all_moves[0]
def is_enemy_win():
"""Returns True if all 3 Musketeers are in the same row or column."""
# Replace with code
m_locations = []
for location in all_locations():
if at(location) == 'M':
m_locations += [location]
return (m_locations[0][0] == m_locations[1][0] and
m_locations[0][0] == m_locations[2][0]
) or (
m_locations[0][1] == m_locations[1][1] and
m_locations[0][1] == m_locations[2][1])
#---------- Communicating with the user ----------
#----you do not need to modify code below unless you find a bug
#----a bug in it before you move to stage 3
def print_board():
print(" 1 2 3 4 5")
print(" ---------------")
ch = "A"
for i in range(0, 5):
print(ch, "|", end = " ")
for j in range(0, 5):
print(board[i][j] + " ", end = " ")
print()
ch = chr(ord(ch) + 1)
print()
def print_instructions():
print()
print("""To make a move, enter the location of the piece you want to move,
and the direction you want it to move. Locations are indicated as a
letter (A, B, C, D, or E) followed by an integer (1, 2, 3, 4, or 5).
Directions are indicated as left, right, up, or down (or simply L, R,
U, or D). For example, to move the Musketeer from the top right-hand
corner to the row below, enter 'A5 left' (without quotes).
For convenience in typing, you may use lowercase letters.""")
print()
def choose_users_side():
"""Returns 'M' if user is playing Musketeers, 'R' otherwise."""
user = ""
while user != 'M' and user != 'R':
answer = input("Would you like to play Musketeer (M) or enemy (R)? ")
answer = answer.strip()
if answer != "":
user = answer.upper()[0]
return user
def get_users_move():
"""Gets a legal move from the user, and returns it as a
(location, direction) tuple."""
directions = {'L':'left', 'R':'right', 'U':'up', 'D':'down'}
move = input("Your move? ").upper().replace(' ', '')
if (len(move) >= 3
and move[0] in 'ABCDE'
and move[1] in '12345'
and move[2] in 'LRUD'):
location = string_to_location(move[0:2])
direction = directions[move[2]]
if is_legal_move(location, direction):
return (location, direction)
print("Illegal move--'" + move + "'")
return get_users_move()
def move_musketeer(users_side):
"""Gets the Musketeer's move (from either the user or the computer)
and makes it."""
if users_side == 'M':
(location, direction) = get_users_move()
if at(location) == 'M':
if is_legal_move(location, direction):
make_move(location, direction)
describe_move("Musketeer", location, direction)
else:
print("You can't move there!")
return move_musketeer(users_side)
else: # Computer plays Musketeer
(location, direction) = choose_computer_move('M')
make_move(location, direction)
describe_move("Musketeer", location, direction)
def move_enemy(users_side):
"""Gets the enemy's move (from either the user or the computer)
and makes it."""
if users_side == 'R':
(location, direction) = get_users_move()
if at(location) == 'R':
if is_legal_move(location, direction):
make_move(location, direction)
describe_move("Enemy", location, direction)
else:
print("You can't move there!")
return move_enemy(users_side)
else: # Computer plays enemy
(location, direction) = choose_computer_move('R')
make_move(location, direction)
describe_move("Enemy", location, direction)
return board
def describe_move(who, location, direction):
"""Prints a sentence describing the given move."""
new_location = adjacent_location(location, direction)
print(who, 'moves', direction, 'from',\
location_to_string(location), 'to',\
location_to_string(new_location) + ".\n")
def start():
"""Plays the Three Musketeers Game."""
users_side = choose_users_side()
board = create_board()
print_instructions()
print_board()
while True:
if has_some_legal_move_somewhere('M'):
board = move_musketeer(users_side)
print_board()
if is_enemy_win():
print("Cardinal Richleau's men win!")
break
else:
print("The Musketeers win!")
break
if has_some_legal_move_somewhere('R'):
board = move_enemy(users_side)
print_board()
else:
print("The Musketeers win!")
break