-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbattleshipmain_vs_cpu.py
391 lines (342 loc) · 14.8 KB
/
battleshipmain_vs_cpu.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
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
"""Global Variables"""
all_ships_dict = {"Carrier": 5 , "Battleship" : 4 , "Cruiser" : 3, "Submarine" : 3 , "Destroyer" : 2}
shot_list_P1 = []
shot_list_CPU = []
ls_all_ships_points_P1 = []
ls_all_ships_points_CPU = []
CPU_target = []
#Setup Phase
class Point:
def __init__(self, initX, initY):
self.x = initX
self.y = initY
"""Board Setup"""
import matplotlib.pyplot as plt
import matplotlib.patches as patches
board = plt.figure(figsize=[9,9])
board.patch.set_facecolor((1,1,.8))
ax = board.add_subplot(111)
# draw the grid
for x in range(11):
ax.plot([x, x], [0,10], 'k')
for y in range(11):
ax.plot([0, 10], [y,y], 'k')
# scale the axis area to fill the whole figure
ax.set_position([0,0,1,1])
# get rid of axes and everything (the figure background will show through)
ax.set_axis_off()
# scale the plot area conveniently (the board is in 0,0..18,18)
ax.set_xlim(-1,11)
ax.set_ylim(-1,11)
# add axis labels to the axes
x_axis_letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K'] #x-axis names for plotting
for i in range(10):
ax.text(i+0.4, -0.4, x_axis_letters[i])
ax.text(-0.4, i+0.4, list(range(10))[i])
def turn_black(draw_ls):
#given a list of Point objects, show the board
for square_point in draw_ls:
chosen_square = str(square_point.x + square_point.y)
#make a dictionary: key = input letter, value = what it would mean as an x-coordinate
letter_to_xcoord_dict = {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7, 'J': 8, 'K': 9}
#convert the letter into an x-coordinate number
chosen_xcoord = letter_to_xcoord_dict[chosen_square[0]]
square_tup = (chosen_xcoord, int(chosen_square[1]))
print(square_tup)
# create a black rectangle patch at (coord.x, coord.y)
rect = patches.Rectangle( square_tup, 1, 1, angle = 0.0, facecolor = 'k')
# add the patch to the axes
ax.add_patch(rect)
"""P1 Setup"""
def check_valid_point_P1(point_str, thing):
#returns False if string is in invalid format, True if valid.
if (len(point_str) != 2) or (point_str[0].upper() not in "ABCDEFGHJK") or (point_str[1] not in "0123456789"):
print("Please enter valid coordinates! e.g. B1")
return False
#Check if thing is within the board using ASCII index
elif (ord(point_str[0]) > 75 or ord(point_str[0]) < 65 or ord(point_str[1]) > 57 or ord(point_str[1]) < 48):
print(thing, " is out of board!")
return False
else:
return True
def place_stern_P1(ship_name):
stern_bool = False
while stern_bool == False :
stern_str = input("Where would you like the stern of your {} to be? e.g. A0. 'I' is not valid.".format(ship_name))#A0
stern_str = stern_str.upper()
stern_bool = check_valid_point_P1(stern_str, "Stern") #check if string is garbage
if stern_bool == True:
return Point( stern_str[0] , stern_str[1] )
#returns stern coordinates as a Point object
def generate_ship_sections_P1(stern, ship_name, size ):
direction_bool = False
ls_points = [stern]
while direction_bool == False:
#Generating ships points based on direction
direction = input("Which direction would you like your {} to face? [N/S/E/W] ".format(ship_name))
direction = direction.upper()
if direction in "NSEW":
for i in range(1, size):
if direction == "N":
ls_points.append(Point(stern.x, chr(ord(stern.y) - i)))
elif direction == "W":
ls_points.append(Point(chr(ord(stern.x) + i) , stern.y))
elif direction == "S":
ls_points.append(Point(stern.x, chr(ord(stern.y) + i)))
elif direction == "E":
ls_points.append(Point(chr(ord(stern.x) - i) , stern.y))
direction_bool = True
else:
print("Please type [N/S/E/W]!")
direction_bool = False
return ls_points
def check_ship_sections_P1(ship_name, ls_points):
points_valid = True
#use ASCII numbering to check
for point in ls_points:
#if point is out of board, break for loop
point_str = point.x + point.y
if not check_valid_point_P1(point_str, ship_name):
points_valid = False
break
#check if point conflicts with another ship's points
elif point in ls_all_ships_points_P1:
print(ship_name, " overlaps an existing ship!")
points_valid = False
break
else:
continue
return points_valid
def place_ship_P1(ship_name):
global all_ships_dict
global ls_all_ships_points_P1 # necessary to check if current ship conflicts with previously placed ship
size = all_ships_dict.get(ship_name) #get ship's size from dictionary
stern = place_stern_P1(ship_name) #ask player for stern position
ship_placed = False
while ship_placed == False:
#generate list of ship points based on known stern and requested direction
ls_points = generate_ship_sections_P1(stern, ship_name, size)
#check if the ships points are valid based on the known board and known ships
points_valid = check_ship_sections_P1(ship_name, ls_points)
#if any of the ship's points are invalid, points_valid = False and the for loop breaks, restarting the while loop
#If the for loop doesn't break, all ship points are valid and we set direction_bool to True to break the while loop
if points_valid:
ship_placed = True
print("Ship placed!") #tell the player the ship is placed
for i in ls_points: #add the ship's points to list of known ships points
ls_all_ships_points_P1.append(i)
return [ship_name, ls_points]
"""CPU setup"""
def check_valid_point_CPU(point_str):
#Check if thing is within the board using ASCII index
if (ord(point_str[0]) > 75 or ord(point_str[0]) < 65 or ord(point_str[1]) > 57 or ord(point_str[1]) < 48):
return False
else:
return True
###
import random
###
def generate_random_point_str():
ls_valid_points = []
for char in "ABCDEFGHJK":
for num in "0123456789":
ls_valid_points.append(char+num)
return random.choice(ls_valid_points)
def place_stern_CPU(ship_name):
stern_str = generate_random_point_str()
return Point( stern_str[0] , stern_str[1] ) #returns stern coordinates as a Point object
def generate_ship_sections_CPU(stern, ship_name, size ):
ls_points = [stern]
#Generating ships points based on random direction
valid_dir_ls = ["N", "S","E", "W"]
direction = random.choice(valid_dir_ls)
for i in range(1, size):
if direction == "N":
ls_points.append(Point(stern.x, chr(ord(stern.y) - i)))
elif direction == "W":
ls_points.append(Point(chr(ord(stern.x) + i) , stern.y))
elif direction == "S":
ls_points.append(Point(stern.x, chr(ord(stern.y) + i)))
elif direction == "E":
ls_points.append(Point(chr(ord(stern.x) - i) , stern.y))
return ls_points
def check_ship_sections_CPU(ship_name, ls_points):
points_valid = True
#use ASCII numbering to check
for point in ls_points:
#if point is out of board, break for loop
point_str = point.x + point.y
if not check_valid_point_CPU(point_str):
points_valid = False
break
#check if point conflicts with another ship's points
elif point in ls_all_ships_points_P1:
points_valid = False
break
else:
continue
return points_valid
def place_ship_CPU(ship_name):
global all_ships_dict
global ls_all_ships_points_CPU # necessary to check if current ship conflicts with previously placed ship
size = all_ships_dict.get(ship_name) #get ship's size from dictionary
stern = place_stern_CPU(ship_name) #ask player for stern position
ship_placed = False
while ship_placed == False:
#generate list of ship points based on known stern and requested direction
ls_points = generate_ship_sections_CPU(stern, ship_name, size)
#check if the ships points are valid based on the known board and known ships
points_valid = check_ship_sections_CPU(ship_name, ls_points)
#if any of the ship's points are invalid, points_valid = False and the for loop breaks, restarting the while loop
#If the for loop doesn't break, all ship points are valid and we set direction_bool to True to break the while loop
if points_valid:
ship_placed = True
print("Ship placed!") #CPU placed ship
for i in ls_points: #add the ship's points to list of known ships points
ls_all_ships_points_CPU.append(i)
return [ship_name, ls_points]
"""War Phase"""
"""P1 functions"""
def check_score(enemy_ship_dict): #check if any enemy ships were sunk
score = 0
for ls_points in enemy_ship_dict.values():
if ls_points == []:
score +=1
return score
def attack(): #returns a Point object with a valid shot.
global shot_list_P1
verified_shot = False
while verified_shot == False:
shot_str = input("Attack coordinates? e.g. A0 ")
shot_str = shot_str.upper()
if check_valid_point_P1(shot_str, "Shot"): #check if string is garbage
shot = Point(shot_str[0], shot_str[1]) #save the shot as Point object
if shot in shot_list_P1:
print("You have taken this shot before! Choose another!")
verified_shot = False
else:
print("Shooting...")
#sleep(3)
verified_shot = True
else:
verified_shot = False
shot_list_P1.append(shot)
return shot
def check_hit(shot, enemy_ship_dict, enemy_ls_all_ships_points):
if shot not in enemy_ls_all_ships_points: #check if shot hit anything
print("You missed.")
else: #finds the enemy ship with the shot and removes it from their list
print("Shot to {} was succesful!".format(str(shot.x + shot.y)))
for ls_points in enemy_ship_dict.values():
if shot in ls_points:
ls_points.remove(shot)
"""CPU functions"""
def attack_CPU_random():
verified_shot = False
while verified_shot == False:
shot_str = generate_random_point_str()
shot = Point(shot_str[0], shot_str[1]) #save the shot as Point object
if shot in shot_list_CPU:
verified_shot = False
else:
print("CPU shooting...")
verified_shot = True
return shot
def generate_add_shot(shot, valid_dir_ls):
#Generating addiional strike locations as a dict with direction : Point
add_shot_dict = {}
for direction in valid_dir_ls:
if direction == "N":
add_shot_dict[Point(shot.x, chr(ord(shot.y) - 1))] = "N"
elif direction == "W":
add_shot_dict[Point(chr(ord(shot.x) + 1) , shot.y)] = "W"
elif direction == "S":
add_shot_dict[Point(shot.x, chr(ord(shot.y) + 1))] = "S"
elif direction == "E":
add_shot_dict[Point(chr(ord(shot.x) - 1) , shot.y)] = "E"
for point, direction in add_shot_dict.items(): #should try subtracting away and use a dictionary. useful to know the direction to whack
if (not check_valid_point_CPU(str(point.x + point.y))) or (point in shot_list_CPU):
del add_shot_dict[point]
return add_shot_dict
def check_hit_CPU(shot, enemy_ship_dict, enemy_ls_all_ships_points, target_dict):
if shot in target_dict.keys():
if shot not in enemy_ls_all_ships_points: #target miss
print("CPU missed.")
#delete from target_dict and try another
del target_dict[shot]
return target_dict
else: #target hit
for ship_name, ls_points in enemy_ship_dict.items(): #finds the enemy ship with the shot and removes it from their list
if shot in ls_points:
ls_points.remove(shot)
print("CPU hit", ship_name, "at", str(shot.x) +str(shot.y))
#delete from target_dict and attack in known direction
shot_dir = target_dict[shot]
return generate_add_shot(shot, [shot_dir])
else: #check for random shot
if shot not in enemy_ls_all_ships_points: #random shot miss
print("CPU missed.")
return {}
else: #random shot hit
for ship_name, ls_points in enemy_ship_dict.items(): #finds the enemy ship with the shot and removes it from their list
if shot in ls_points:
ls_points.remove(shot)
print("CPU hit", ship_name, "at", str(shot.x) +str(shot.y))
return generate_add_shot(shot, ["N", "S","E", "W"] ) #len 2 to 4
"""main function"""
def main():
game_not_over = True
#Setting player ships
P1_ship_dict = {}
CPU_ship_dict = {}
round_num = 0
for ship_name in all_ships_dict:
some_ship = place_ship_P1(ship_name)
some_ship_name = some_ship[0]
some_ls_points = some_ship[1]
P1_ship_dict[some_ship_name] = some_ls_points
print("{} written to {}!".format(some_ls_points, some_ship_name))
turn_black(ls_all_ships_points_P1)
plt.show()
#Setting CPU ships
for ship_name in all_ships_dict:
some_ship_CPU = place_ship_CPU(ship_name)
some_ship_name = some_ship[0]
some_ls_points = some_ship_CPU[1]
CPU_ship_dict[some_ship_name] = some_ls_points
#War Phase
starting_player = random.choice(["P1", "CPU"])
target_dict = {}
if starting_player == "P1":
player_turn = True
else:
player_turn = False
while game_not_over:
round_num += 1
print("Round", round_num)
if player_turn:
print("Player's Turn!")
shot = attack()
check_hit(shot, CPU_ship_dict, ls_all_ships_points_CPU)
player_turn = False
else:
print("CPU's Turn!")
if target_dict == {}: #no targets
CPU_shot = attack_CPU_random()
else: #attack CPU target
CPU_shot = random.choice(target_dict.keys())
target_dict = check_hit_CPU(CPU_shot, P1_ship_dict, ls_all_ships_points_P1, target_dict) #update target_dict
shot_list_CPU.append(CPU_shot)
player_turn = True
#CPU checks for game end
CPU_score = check_score(P1_ship_dict)
P1_score = check_score(CPU_ship_dict)
if CPU_score == 5:
print("You lost!")
game_not_over == False # what if they are 5 at the same time
elif P1_score == 5:
print("You won!")
game_not_over == False
else:
game_not_over == True
main()