taylorlinton commited on
Commit
9fb2335
·
verified ·
1 Parent(s): 8ed2fb1

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +248 -0
app.py ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ import gradio as gr
3
+ import matplotlib.pyplot as plt
4
+ import numpy as np
5
+
6
+ BOARD_SIZE = 5
7
+ NUM_SHIPS = 3
8
+
9
+ def place_ships(board, num_ships):
10
+ """ Randomly place 'num_ships' single-cell ships on 'board'. """
11
+ placed = 0
12
+ while placed < num_ships:
13
+ r, c = random.randint(0, BOARD_SIZE - 1), random.randint(0, BOARD_SIZE - 1)
14
+ if board[r][c] == 0: # empty spot
15
+ board[r][c] = 1
16
+ placed += 1
17
+
18
+ def init_game():
19
+ """ Create initial state for a new Battleship game. """
20
+ user_board = [[0] * BOARD_SIZE for _ in range(BOARD_SIZE)]
21
+ ai_board = [[0] * BOARD_SIZE for _ in range(BOARD_SIZE)]
22
+
23
+ place_ships(user_board, NUM_SHIPS)
24
+ place_ships(ai_board, NUM_SHIPS)
25
+
26
+ return {
27
+ "user_board": user_board,
28
+ "ai_board": ai_board,
29
+ "user_ships_remaining": NUM_SHIPS,
30
+ "ai_ships_remaining": NUM_SHIPS,
31
+ "game_over": False,
32
+ "message": "Game started! Enter row and column to fire.",
33
+ "ai_guesses": set()
34
+ }
35
+
36
+ def plot_board(board, is_user_board=True):
37
+ """
38
+ Visual representation of the game board using Matplotlib.
39
+ Different colors are used for ships, hits, and misses.
40
+ """
41
+ fig, ax = plt.subplots(figsize=(5, 5))
42
+ colors = {
43
+ 0: "#D3D3D3", # Empty - Light Gray
44
+ 1: "#1E90FF" if is_user_board else "#D3D3D3", # Ship - Blue for User, Hidden for AI
45
+ 2: "#FF0000", # Hit - Red
46
+ 3: "#FFFFFF" # Miss - White
47
+ }
48
+
49
+ grid = np.array(board)
50
+ if not is_user_board:
51
+ # Hide AI's ships by turning cells with '1' into '0'
52
+ grid = np.where(grid == 1, 0, grid)
53
+
54
+ colored_grid = np.vectorize(colors.get)(grid)
55
+ for r in range(BOARD_SIZE):
56
+ for c in range(BOARD_SIZE):
57
+ rect = plt.Rectangle((c, BOARD_SIZE - r - 1), 1, 1,
58
+ facecolor=colored_grid[r][c],
59
+ edgecolor="black")
60
+ ax.add_patch(rect)
61
+
62
+ ax.set_xticks(np.arange(BOARD_SIZE) + 0.5, labels=[str(i) for i in range(BOARD_SIZE)])
63
+ ax.set_yticks(np.arange(BOARD_SIZE) + 0.5, labels=[str(i) for i in range(BOARD_SIZE)][::-1])
64
+ ax.set_xticks(np.arange(BOARD_SIZE + 1), minor=True)
65
+ ax.set_yticks(np.arange(BOARD_SIZE + 1), minor=True)
66
+ ax.grid(which="minor", color="black", linestyle='-', linewidth=1)
67
+ ax.set_xlim(0, BOARD_SIZE)
68
+ ax.set_ylim(0, BOARD_SIZE)
69
+ ax.set_frame_on(False)
70
+
71
+ return fig
72
+
73
+ def plot_color_key():
74
+ """ Generates a small color-coded key explaining the board symbols. """
75
+ fig, ax = plt.subplots(figsize=(5, 1))
76
+ ax.set_xlim(0, 4)
77
+ ax.set_ylim(0, 1)
78
+ color_labels = [
79
+ ("#1E90FF", "Your Ship"),
80
+ ("#FF0000", "Hit Ship"),
81
+ ("#FFFFFF", "Miss"),
82
+ ("#D3D3D3", "Hidden")
83
+ ]
84
+
85
+ for i, (color, label) in enumerate(color_labels):
86
+ rect = plt.Rectangle((i, 0), 1, 1, facecolor=color, edgecolor="black")
87
+ ax.add_patch(rect)
88
+ ax.text(i + 0.5, 0.5, label, ha='center', va='center', fontsize=10)
89
+
90
+ ax.set_xticks([])
91
+ ax.set_yticks([])
92
+ ax.set_frame_on(False)
93
+
94
+ return fig
95
+
96
+ def ai_guess(state):
97
+ """ AI randomly picks an unguessed cell in the user's board and marks hit/miss. """
98
+ user_board = state["user_board"]
99
+ valid_positions = [
100
+ (r, c) for r in range(BOARD_SIZE)
101
+ for c in range(BOARD_SIZE)
102
+ if (r, c) not in state["ai_guesses"]
103
+ ]
104
+ if not valid_positions:
105
+ return
106
+
107
+ r, c = random.choice(valid_positions)
108
+ state["ai_guesses"].add((r, c))
109
+
110
+ if user_board[r][c] == 1:
111
+ user_board[r][c] = 2 # AI hit
112
+ state["user_ships_remaining"] -= 1
113
+ state["message"] += f"\nAI hit your ship at ({r}, {c})!"
114
+ else:
115
+ user_board[r][c] = 3 # AI miss
116
+ state["message"] += f"\nAI missed at ({r}, {c})."
117
+
118
+ def make_move(state, row, col):
119
+ """ Handles the user's move, updates the board, and checks win conditions. """
120
+ if state["game_over"]:
121
+ return (
122
+ plot_board(state["user_board"]),
123
+ plot_board(state["ai_board"], is_user_board=False),
124
+ state["message"],
125
+ plot_color_key()
126
+ )
127
+
128
+ # Validate row/col input
129
+ if not (0 <= row < BOARD_SIZE and 0 <= col < BOARD_SIZE):
130
+ state["message"] = "Invalid input. Row/Col must be between 0 and 4."
131
+ return (
132
+ plot_board(state["user_board"]),
133
+ plot_board(state["ai_board"], is_user_board=False),
134
+ state["message"],
135
+ plot_color_key()
136
+ )
137
+
138
+ # Check if user already attacked this cell
139
+ if state["ai_board"][row][col] in (2, 3):
140
+ state["message"] = f"You already attacked ({row}, {col})!"
141
+ return (
142
+ plot_board(state["user_board"]),
143
+ plot_board(state["ai_board"], is_user_board=False),
144
+ state["message"],
145
+ plot_color_key()
146
+ )
147
+
148
+ # If there's a ship at the target
149
+ if state["ai_board"][row][col] == 1:
150
+ state["ai_board"][row][col] = 2 # User hit
151
+ state["ai_ships_remaining"] -= 1
152
+ state["message"] = f"Hit! You hit AI's ship at ({row}, {col})."
153
+ else:
154
+ state["ai_board"][row][col] = 3 # User miss
155
+ state["message"] = f"Miss! No ship at ({row}, {col})."
156
+
157
+ # Check if user just won
158
+ if state["ai_ships_remaining"] == 0:
159
+ state["message"] += "\nYou sank all the AI's ships. You win!"
160
+ state["game_over"] = True
161
+ return (
162
+ plot_board(state["user_board"]),
163
+ plot_board(state["ai_board"], is_user_board=False),
164
+ state["message"],
165
+ plot_color_key()
166
+ )
167
+
168
+ # Otherwise, let the AI guess
169
+ ai_guess(state)
170
+
171
+ # Check if AI just won
172
+ if state["user_ships_remaining"] == 0:
173
+ state["message"] += "\nAI sank all your ships. You lose!"
174
+ state["game_over"] = True
175
+
176
+ return (
177
+ plot_board(state["user_board"]),
178
+ plot_board(state["ai_board"], is_user_board=False),
179
+ state["message"],
180
+ plot_color_key()
181
+ )
182
+
183
+ def reset_game():
184
+ """ Reset the game state completely. """
185
+ return init_game()
186
+
187
+ def update_display(state):
188
+ """
189
+ Helper function to pull out the boards and message
190
+ so we can show them immediately or after any reset.
191
+ """
192
+ return (
193
+ plot_board(state["user_board"]),
194
+ plot_board(state["ai_board"], is_user_board=False),
195
+ state["message"],
196
+ plot_color_key()
197
+ )
198
+
199
+ with gr.Blocks() as demo:
200
+ gr.Markdown("""
201
+ # Battleship Game
202
+
203
+ **Welcome to Battleship!**
204
+
205
+ In your opponent's 5x5 grid, there are three hidden ships (size=1).
206
+ Select the row (0-4) and column (0-4) you want to fire at, then hit "Fire!"
207
+ Follow the messages to see who gets hit and eventually wins.
208
+ """)
209
+
210
+ # Initialize game state
211
+ state = gr.State(init_game())
212
+
213
+ # Layout
214
+ with gr.Row():
215
+ with gr.Column():
216
+ user_board_display = gr.Plot(label="Your Board")
217
+ with gr.Column():
218
+ ai_board_display = gr.Plot(label="AI Board")
219
+
220
+ message_display = gr.Textbox(label="Game Messages", interactive=False)
221
+ color_key_display = gr.Plot(label="Color Key") # Will be updated automatically
222
+
223
+ row_in = gr.Number(label="Row (0-4)", value=0, precision=0)
224
+ col_in = gr.Number(label="Column (0-4)", value=0, precision=0)
225
+ fire_button = gr.Button("Fire!")
226
+ reset_button = gr.Button("Reset Game")
227
+
228
+ # Render boards on initial load
229
+ demo.load(fn=update_display, inputs=state,
230
+ outputs=[user_board_display, ai_board_display, message_display, color_key_display])
231
+
232
+ # Fire action
233
+ fire_button.click(
234
+ fn=make_move,
235
+ inputs=[state, row_in, col_in],
236
+ outputs=[user_board_display, ai_board_display, message_display, color_key_display]
237
+ )
238
+
239
+ # Reset action
240
+ # 1. Reset the state
241
+ # 2. Then re-render the boards using update_display
242
+ reset_button.click(fn=reset_game, inputs=[], outputs=state).then(
243
+ fn=update_display,
244
+ inputs=state,
245
+ outputs=[user_board_display, ai_board_display, message_display, color_key_display]
246
+ )
247
+
248
+ demo.launch()