Skip to content

Commit d62bfd9

Browse files
author
Mark Charlton
committed
Wk8 Destroyer completed
1 parent e02fa99 commit d62bfd9

10 files changed

+3452
-2
lines changed
Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
# single ship battleship game.
2+
3+
## DEBUG FLAG
4+
f_debug = 0
5+
6+
import pprint as pp
7+
import random as r
8+
import re
9+
10+
# Screen control params
11+
UP = '\033[1A'
12+
UP2 = '\033[2A'
13+
CLEAR = '\x1b[2K'
14+
RESET = '\033[2J'
15+
BLINKON = '\033[32;5m'
16+
BLINKOFF = '\033[0m'
17+
REVON = "\033[7m"
18+
REVOFF = "\033[0m"
19+
REDON= "\033[31m" # Red text.
20+
GREENON = "\033[32m" # Green text.
21+
COLOUROFF = "\033[0m" #Reset special formatting (such as colour).
22+
#print ( UP, end=CLEAR)
23+
24+
########################################
25+
#
26+
# Row == Y == Height == R
27+
# Col == X == Width == C
28+
#
29+
########################################
30+
31+
def init_board(bw, bh):
32+
print ( f'Creating a board {bw}*{bh} ' )
33+
board=[[' ' for j in range(bw)] for i in range(bh)]
34+
targets=[['-' for j in range(bw)] for i in range(bh)]
35+
return board,targets
36+
37+
def print_board(f_live = 1):
38+
if f_live==0:
39+
this_board=targets
40+
else:
41+
this_board=board
42+
43+
print (RESET)
44+
45+
s_output = f"You have sunk {ships_sunk} / {num_placed_ships} ship(s).\n"
46+
s_output += f"There are {parts_to_sink} remaining ship part(s) to hit.\n\n"
47+
48+
s_output += REVON + " R C"
49+
for xc in range(board_width):
50+
s_output += f' {xc+1} '
51+
s_output += ' C ' + REVOFF + '\n'
52+
53+
for yr in range(board_height):
54+
s_output += REVON + ' ' + str( yr+1 ) + ' ' + REVOFF + ' [' + '] ['.join(this_board[yr]) + ']\n'
55+
s_output += REVON +" R "
56+
s_output += REVOFF +'\n'
57+
58+
#// replace with RegEx
59+
pattern = r'\[[A-Z]\]'
60+
s_output = re.sub(pattern, lambda match: REDON + match.group() + COLOUROFF , s_output)
61+
pattern = r'\[\-\]'
62+
s_output = re.sub(pattern, lambda match: GREENON + match.group() + COLOUROFF , s_output)
63+
64+
print ( s_output )
65+
66+
#/// This places a pre defined array of ships in the grid.
67+
def place_ships():
68+
for key, ship in ships.items():
69+
place_ship ( key, ship )
70+
71+
#/// This places a single ship in the grid.
72+
def place_ship( s_id, a_ship ):
73+
global targets
74+
global parts_to_sink
75+
global ships_placed
76+
77+
if f_debug > 1:
78+
print (" Placing ship : ")
79+
pp.pprint(a_ship)
80+
81+
for plot in a_ship["Locn"]:
82+
targets[plot[0]][plot[1]]=s_id
83+
parts_to_sink +=1
84+
## update to say ship placed.
85+
ships_placed += 1
86+
87+
#/// This creates ships as it goes based on the parameters in the ships array
88+
#/// The maximum length of a ship is the board (width / 2 ) + 1
89+
def create_ships():
90+
global targets
91+
92+
ships_placed = 0
93+
94+
i_max_size=(board_width // 2) + 1
95+
for ship in auto_ships:
96+
if ship["Length"] > i_max_size:
97+
# skip ships too large for the board
98+
continue
99+
else:
100+
for i in range(ship['Qty']):
101+
#// create the ship and place it
102+
ship_locn = find_space ( ship['Length'] )
103+
if ship_locn['Success']==1:
104+
new_ship={"Length": ship['Length'], "Locn": ship_locn['Locn'], "Hits": 0}
105+
ships[ship_ids[ships_placed]]= new_ship
106+
place_ship ( ship_ids[ships_placed], new_ship )
107+
ships_placed += 1
108+
109+
return ships_placed
110+
111+
#// go to the board and find a home for a ship
112+
def find_space(length):
113+
if f_debug > 0:
114+
print ( f"Finding space for a ship {length} long" )
115+
116+
ret_val = {"Success": 0, "Locn": []}
117+
found_spaces = 0
118+
119+
i_iters=0
120+
i_max_iters = 1000
121+
start_r = r.randrange(0, board_height-1)
122+
start_c = r.randrange(0, board_width-1)
123+
124+
#// Decide where you're going and go get something.
125+
dir = r.choice( ['N', 'E', 'S', 'W'] )
126+
127+
while True:
128+
i_iters += 1
129+
130+
if f_debug > 1:
131+
print ( f'Testing {start_r},{start_c}' )
132+
133+
if targets[start_r][start_c]=="-":
134+
ret_val['Locn'].append([start_r, start_c])
135+
yr, xc = start_r, start_c
136+
found_spaces = 1
137+
done_swap = 0
138+
139+
while found_spaces < length:
140+
yr, xc =next_space(yr, xc , dir)
141+
# if its off the board, or already occupied then flip direction and try again
142+
if xc < 0 or xc >= board_width or yr < 0 or yr >= board_height or targets[yr][xc] != "-":
143+
if f_debug > 0:
144+
print (f'Out of range error or taken {yr},{xc}')
145+
146+
done_swap += 1
147+
dir = swap_direction( dir )
148+
yr, xc = start_r, start_c
149+
# if you've tried to swap already, fail.
150+
if done_swap > 1:
151+
ret_val['Locn'].clear()
152+
break
153+
else:
154+
continue
155+
else :
156+
ret_val['Locn'].append([yr, xc])
157+
found_spaces += 1
158+
159+
if found_spaces == length: # if it worked, mark as success
160+
ret_val['Success']=1
161+
# exit the infinite while to return the status.
162+
break
163+
else:
164+
# Space is not free so fail this route and try again .
165+
ret_val['Locn'].clear()
166+
167+
if i_iters >= i_max_iters:
168+
ret_val['Success']= -1 # mark as iterations failure
169+
break
170+
else:
171+
start_r = r.randrange(0, board_height-1)
172+
start_c = r.randrange(0, board_width-1)
173+
i_iters += 1
174+
175+
else:
176+
start_r = r.randrange(0, board_height-1)
177+
start_c = r.randrange(0, board_width-1)
178+
dir = r.choice( ['N', 'E', 'S', 'W'] )
179+
found_spaces = 0
180+
done_swap = 0
181+
i_iters += 1
182+
183+
if i_iters >= i_max_iters:
184+
ret_val['Success']= -2 # mark as iterations failure
185+
break
186+
187+
if f_debug > 1:
188+
print ( ret_val )
189+
190+
#// just quit and return the values.
191+
return ret_val
192+
193+
#/// Stolen from ChatGPT (v4) and adapted to see how it can write a function.
194+
#/// I can write it a few other ways, but thought I'd keep this one, its a bit overly complex but works.
195+
def swap_direction(direction):
196+
# Create a dictionary to map the values to their translated values
197+
translation_dict = dict([('N', 'S'),('S', 'N'), ('E', 'E'), ('W', 'E')])
198+
direction = direction.upper()
199+
200+
# Swap the direction if it exists in the translation dictionary
201+
if direction in translation_dict:
202+
return translation_dict[direction]
203+
else:
204+
return direction
205+
206+
def next_space(yr, xc, dir):
207+
if dir == 'N':
208+
yr += -1
209+
elif dir == 'E':
210+
xc += 1
211+
elif dir == 'S':
212+
yr += 1
213+
elif dir == 'W':
214+
xc += -1
215+
return yr, xc
216+
217+
def take_shot(s_shot):
218+
global board
219+
global ships
220+
global parts_to_sink
221+
global ships_sunk
222+
223+
# remember to subtract 1 from the shot to shift the array into the right location
224+
shot_r = int( s_shot[0] ) - 1
225+
shot_c = int( s_shot[1] ) - 1
226+
227+
board[shot_r][shot_c]=targets[shot_r][shot_c]
228+
229+
if f_debug > 0 :
230+
pp.pprint (board)
231+
z=input("ENTER")
232+
233+
print_board ( )
234+
235+
if board[shot_r][shot_c]!="-":
236+
parts_to_sink -=1
237+
print ( f'Congratulations that was a hit' )
238+
ships[board[shot_r][shot_c]]["Hits"] += 1
239+
if ships[board[shot_r][shot_c]]["Hits"] == ships[board[shot_r][shot_c]]["Length"]:
240+
print ( f"You've sunk ship {board[shot_r][shot_c]}! Only {(ships_placed-ships_sunk)-1} ships to go." )
241+
ships_sunk += 1
242+
else :
243+
print ( f'Unlucky, you missed that time' )
244+
245+
#input ("Press ENTER to continue")
246+
247+
def get_shot():
248+
global exit_flag
249+
s_err=""
250+
251+
while True:
252+
if s_err > '':
253+
print_board ()
254+
255+
s_shot = input ( f"{s_err}Please enter your target (RC e.g. 10) [Q to exit]: ")
256+
257+
# deal with premature enter key hitting
258+
if s_shot == "":
259+
s_err =""
260+
continue
261+
262+
if s_shot.upper() == "Q":
263+
exit_flag=1
264+
break
265+
266+
if len(s_shot) != 2:
267+
s_err = f"Invalid Coordinates {s_shot} \n "
268+
continue
269+
270+
shot_x = int( s_shot[0] ) - 1
271+
shot_y = int( s_shot[1] ) - 1
272+
273+
if (shot_x < 0 or shot_x >= board_height) or (shot_y < 0 or shot_y >= board_width):
274+
s_err = f"Invalid Coordinates {s_shot} [{str(shot_x)},{str(shot_y)}]\n "
275+
continue
276+
277+
if board[shot_x][shot_y] != " ":
278+
s_err = "Coordinates already targeted\n "
279+
continue
280+
281+
break
282+
283+
return s_shot
284+
285+
def check_exit():
286+
f_check=input("Are you sure you wish to quit? (Y/N) :").upper()
287+
return f_check
288+
289+
# inits
290+
# // only works up to a 9x9 currently
291+
292+
293+
# program quit flag
294+
# used by functions to flag the need to quit the program
295+
exit_flag = 0
296+
297+
parts_to_sink=0
298+
ships_placed = 0
299+
ships_sunk = 0
300+
ship_ids = "ABCDEFGHIJKLMNOP"
301+
302+
# use S to match the assignment brief e.g. 5x5 board with 1 x 2 space ship in known location
303+
s_game_type = input ( "Do you want the [S]imple (set in assignment) game or [A]utomatic play? [S/A]: ")
304+
s_game_type = s_game_type.upper()
305+
306+
ships={}
307+
308+
if s_game_type == "S":
309+
board_width=5
310+
board_height=5
311+
# Build the array of ships
312+
ships["A"] = ({"Length": 2, "Locn": [[2,2],[2,3]], "Hits": 0})
313+
else:
314+
# this doesn't bother with any input validation.
315+
# I could use my get_int helper here.
316+
# invalid input with crash the program
317+
user_board = input("Please enter the board dimensions RxC (e.g. 5x5, 4x9) [MIN 4x4, MAX 9x9]: ")
318+
board_height, board_width = int(user_board[0]), int(user_board[2])
319+
320+
# sanitise sizes
321+
if board_width < 4: board_width=4
322+
if board_width > 9: board_width=9
323+
if board_height < 4: board_height=4
324+
if board_height > 9: board_height=9
325+
326+
# Build the automatic ships array
327+
auto_ships=[]
328+
auto_ships.append({'Length': 5, 'Qty': 1})
329+
auto_ships.append({'Length': 4, 'Qty': 1})
330+
auto_ships.append({'Length': 3, 'Qty': 2})
331+
auto_ships.append({'Length': 2, 'Qty': 3})
332+
333+
# Setup the game
334+
board,targets = init_board( board_width , board_height )
335+
336+
if f_debug > 1:
337+
pp.pprint ( board )
338+
pp.pprint ( targets )
339+
a = input ("Press ENTER to continue: ")
340+
341+
# on automatic you need to create the ships to place
342+
if s_game_type == "A":
343+
create_ships ( )
344+
else:
345+
place_ships ( )
346+
347+
num_placed_ships = len(ships)
348+
349+
if f_debug > 0:
350+
pp.pprint ( ships )
351+
pp.pprint ( targets )
352+
a = input ("Press ENTER to continue: ")
353+
354+
print_board ( )
355+
356+
# Let the target practice commence
357+
while True:
358+
s_shot = get_shot()
359+
if s_shot.upper() == 'Q':
360+
print_board ( 0 )
361+
print ( "Thanks for playing " )
362+
break
363+
take_shot ( s_shot )
364+
365+
if parts_to_sink == 0:
366+
print ( f"\n{BLINKON}Congrats!{BLINKOFF} You've taken them all out.\n\n")
367+
break

0 commit comments

Comments
 (0)