Skip to content

Commit e97c00c

Browse files
author
Mark Charlton
committed
wk8 destroyer - amend init prompt
1 parent d62bfd9 commit e97c00c

File tree

7 files changed

+1609
-3
lines changed

7 files changed

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

0 commit comments

Comments
 (0)