Pash1986 commited on
Commit
803c131
·
verified ·
1 Parent(s): c12be98

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +71 -14
  2. app.py +285 -0
  3. requirements.txt +3 -0
README.md CHANGED
@@ -1,14 +1,71 @@
1
- ---
2
- title: Ai Battleship Vs Bedrock
3
- emoji: 📊
4
- colorFrom: gray
5
- colorTo: green
6
- sdk: streamlit
7
- sdk_version: 1.40.2
8
- app_file: app.py
9
- pinned: false
10
- license: apache-2.0
11
- short_description: A battle ship AI game powered by MongoDB Atlas and Bedrock
12
- ---
13
-
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Battleship Game
2
+
3
+ This is a Streamlit-based implementation of the classic Battleship game, featuring a player versus an AI opponent powered by AWS Bedrock's Claude model.
4
+
5
+ ## Features
6
+
7
+ - Interactive game board using Streamlit components
8
+ - AI opponent using AWS Bedrock's Claude model
9
+ - Game state persistence using MongoDB Atlas
10
+ - Randomized ship placement
11
+ - Turn-based gameplay
12
+
13
+ ## Prerequisites
14
+
15
+ - Python 3.7+
16
+ - Streamlit
17
+ - pymongo
18
+ - boto3
19
+ - AWS account with Bedrock Claude 3.5 access on `US-EAST-1`.
20
+ - MongoDB Atlas account with your IP in the accesslist permission.
21
+
22
+ ## Installation
23
+
24
+ 1. Clone the repository:
25
+ ```
26
+ cd atlas-ai-battleship-game
27
+ ```
28
+ 2. Install the required packages:
29
+ ```
30
+ pip install -r requirements.txt
31
+ ```
32
+ 3. Set up environment variables:
33
+ - `MONGODB_ATLAS_URI`: Your MongoDB Atlas connection string
34
+ - `AWS_ACCESS_KEY`: Your AWS access key
35
+ - `AWS_SECRET_KEY`: Your AWS secret key
36
+
37
+ ## Running the Game
38
+
39
+ To start the game, run:
40
+ ```
41
+ streamlit run battleship-game.py
42
+ ```
43
+
44
+ ## How to Play
45
+
46
+ 1. The game starts with ships randomly placed on both the player's and opponent's boards.
47
+ 2. Click on the ocean emoji (🌊) on the attack board to make your move.
48
+ 3. The AI opponent (Claude) will make its move automatically.
49
+ 4. The game continues until all ships of one player are sunk.
50
+ 5. You can mark the ships you have sank.
51
+
52
+ ## Code Structure
53
+
54
+ - `main()`: The main function that sets up the Streamlit UI and game flow.
55
+ - `initialize_game()`: Initializes the game state and database entries.
56
+ - `render_board()`: Renders the game boards using Streamlit components.
57
+ - `attack()`: Processes a player's attack.
58
+ - `opponent_turn()`: Handles the AI opponent's turn using Claude.
59
+ - `get_bedrock_claude_move()`: Interacts with AWS Bedrock to get Claude's next move.
60
+ - `update_database()`: Updates the game state in the MongoDB database.
61
+
62
+ ## Future Improvements
63
+
64
+ - Implement a more sophisticated AI strategy
65
+ - Add sound effects and animations
66
+ - Create a multiplayer mode
67
+ - Improve the UI/UX with more detailed ship information and game statistics
68
+
69
+ ## Contributing
70
+
71
+ Contributions are welcome! Please feel free to submit a Pull Request.
app.py ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import random
3
+ import json
4
+ import os
5
+ import boto3
6
+ from pymongo import MongoClient
7
+ from bson.objectid import ObjectId
8
+
9
+ # Constants
10
+ BOARD_SIZE = 10
11
+ EMPTY = ':ocean:'
12
+ HIT = ':boom:'
13
+ MISS = ':heavy_multiplication_x:'
14
+ SHIPS = [
15
+ {"name": "Carrier", "size": 5, "symbol": ":ship:"},
16
+ {"name": "Battleship", "size": 4, "symbol": ":motor_boat:"},
17
+ {"name": "Cruiser", "size": 3, "symbol": ":boat:"},
18
+ {"name": "Submarine", "size": 3, "symbol": ":ferry:"},
19
+ {"name": "Destroyer", "size": 2, "symbol": ":speedboat:"}
20
+ ]
21
+
22
+ # Initialize difficulty in session state if not present
23
+ if 'difficulty' not in st.session_state:
24
+ st.session_state.difficulty = "Beginner"
25
+
26
+ # Add difficulty selector
27
+ st.session_state.difficulty = st.selectbox(
28
+ "Select Difficulty",
29
+ ["Beginner", "Expert"],
30
+ index=0 if st.session_state.difficulty == "Beginner" else 1
31
+ )
32
+
33
+ # MongoDB Atlas Connection
34
+ client = MongoClient(os.environ.get('MONGODB_ATLAS_URI'))
35
+ db = client['battleship']
36
+ games = db['games']
37
+
38
+ region = 'us-east-1' if st.session_state.difficulty == "Begginer" else 'us-west-2'
39
+ # AWS Bedrock Client Setup
40
+ bedrock_runtime = boto3.client('bedrock-runtime',
41
+ aws_access_key_id=os.environ.get('AWS_ACCESS_KEY'),
42
+ aws_secret_access_key=os.environ.get('AWS_SECRET_KEY'),
43
+ region_name=region)
44
+
45
+ def get_bedrock_claude_move(board, openent_moves=None):
46
+ """
47
+ Get the next move from Claude AI using AWS Bedrock.
48
+ """
49
+ print("inside get_bedrock_claude_move")
50
+ claude_body = json.dumps({
51
+ "anthropic_version": "bedrock-2023-05-31",
52
+ "max_tokens": 5000,
53
+ "temperature": 0,
54
+ "system": f"Please provide the next move as an opponent in the battleship game , the board is {BOARD_SIZE} X {BOARD_SIZE} . Be smart and stratigic. Respond only in JSON format strictly: 'row' : ... , 'col' : ... , 'entertainment_comment' and nothing more . ",
55
+ "messages": [{
56
+ "role": "user",
57
+ "content": [
58
+ {"type": "text", "text": f"The current board: {board} and your moves were: {openent_moves if openent_moves else 'None'}"}
59
+ ]
60
+ }]
61
+ })
62
+
63
+ model_id = "anthropic.claude-3-5-sonnet-20241022-v2:0" if st.session_state.difficulty == "Expert" else "anthropic.claude-3-5-sonnet-20240620-v1:0"
64
+
65
+ response = bedrock_runtime.invoke_model(
66
+ body=claude_body,
67
+ modelId=model_id,
68
+ accept="application/json",
69
+ contentType="application/json",
70
+ )
71
+
72
+ response_body = json.loads(response.get("body").read())
73
+ json_response = json.loads(response_body["content"][0]['text'])
74
+
75
+ return json_response['row'], json_response['col'], json_response['entertainment_comment']
76
+
77
+ # Game Setup Functions
78
+ def create_empty_board():
79
+ """Create an empty game board."""
80
+ return [[EMPTY for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)]
81
+
82
+ def place_ships_randomly(board):
83
+ """Place ships randomly on the board."""
84
+ for ship in SHIPS:
85
+ while True:
86
+ row = random.randint(0, BOARD_SIZE - 1)
87
+ col = random.randint(0, BOARD_SIZE - 1)
88
+ direction = random.choice(['horizontal', 'vertical'])
89
+ if can_place_ship(board, row, col, ship['size'], direction):
90
+ place_ship(board, row, col, ship['size'], direction, ship['symbol'])
91
+ break
92
+ return board
93
+
94
+ def can_place_ship(board, row, col, size, direction):
95
+ """Check if a ship can be placed at the given position."""
96
+ if direction == 'horizontal':
97
+ if col + size > BOARD_SIZE:
98
+ return False
99
+ return all(board[row][col+i] == EMPTY for i in range(size))
100
+ else:
101
+ if row + size > BOARD_SIZE:
102
+ return False
103
+ return all(board[row+i][col] == EMPTY for i in range(size))
104
+
105
+ def place_ship(board, row, col, size, direction, symbol):
106
+ """Place a ship on the board."""
107
+ if direction == 'horizontal':
108
+ for i in range(size):
109
+ board[row][col+i] = symbol
110
+ else:
111
+ for i in range(size):
112
+ board[row+i][col] = symbol
113
+
114
+ def initialize_game():
115
+ """Initialize the game state."""
116
+ if 'game_state' not in st.session_state:
117
+ st.session_state.game_state = {
118
+ 'player_board': place_ships_randomly(create_empty_board()),
119
+ 'opponent_board': place_ships_randomly(create_empty_board()),
120
+ 'player_attacks': create_empty_board(),
121
+ 'player_hits_left': 17,
122
+ 'opponent_attacks': create_empty_board(),
123
+ 'openent_moves': [],
124
+ 'opponent_hits_left': 17,
125
+ 'current_player': 'player',
126
+ 'game_state': 'not_started',
127
+ 'game_over': False,
128
+ 'message': ''
129
+ }
130
+
131
+ if 'game_id' not in st.session_state.game_state:
132
+ st.session_state.game_state['game_id'] = ObjectId()
133
+ gameId = st.session_state.game_state['game_id']
134
+
135
+ # Initialize game data in MongoDB
136
+ games.update_one({'game_id': gameId, 'type': 'player'},
137
+ {"$set": {'game_id': gameId,
138
+ 'board': st.session_state.game_state['player_board'],
139
+ 'attacking_board': st.session_state.game_state['player_attacks']}},
140
+ upsert=True)
141
+ games.update_one({'game_id': gameId, 'type': 'opponent'},
142
+ {"$set": {'game_id': gameId,
143
+ 'board': st.session_state.game_state['opponent_board'],
144
+ 'oponent_moves': st.session_state.game_state['openent_moves'],
145
+ 'attacking_board': st.session_state.game_state['opponent_attacks']}},
146
+ upsert=True)
147
+
148
+ def check_game_over():
149
+ """Check if the game is over."""
150
+ game_state = st.session_state.game_state
151
+
152
+ if game_state['player_hits_left'] == 0:
153
+ game_state['message'] = "You won!"
154
+ game_state['game_over'] = True
155
+ return True
156
+ elif game_state['opponent_hits_left'] == 0:
157
+ game_state['message'] = "You lost!"
158
+ game_state['game_over'] = True
159
+ return True
160
+
161
+ def render_board(board, is_opponent=False):
162
+ """Render the game board using Streamlit components."""
163
+ for i, row in enumerate(board):
164
+ cols = st.columns(BOARD_SIZE)
165
+ for j, cell in enumerate(row):
166
+ with cols[j]:
167
+ if is_opponent and cell == EMPTY:
168
+ if st.button('🌊', key=f'opp_{i}_{j}', on_click=lambda r=i, c=j: attack(r, c)):
169
+ pass
170
+ else:
171
+ # Determine the button color based on the cell content
172
+ if cell == EMPTY:
173
+ button_color = 'secondary'
174
+ elif cell in [HIT, MISS]:
175
+ button_color = 'primary' # Use default color for hits and misses
176
+ else:
177
+ button_color = 'primary' if not is_opponent else 'secondary'
178
+
179
+ # Use Streamlit's button with theme colors
180
+ st.button(cell, key=f'{"opp" if is_opponent else "player"}_{i}_{j}', type=button_color)
181
+
182
+ def attack(row, col):
183
+ """Process a player's attack."""
184
+ game_state = st.session_state.game_state
185
+ if game_state['opponent_board'][row][col] != EMPTY:
186
+ game_state['player_attacks'][row][col] = HIT
187
+ game_state['message'] = "You hit a ship!"
188
+ game_state['opponent_hits_left'] -= 1
189
+ else:
190
+ game_state['player_attacks'][row][col] = MISS
191
+ game_state['message'] = "You missed!"
192
+
193
+ update_database('player')
194
+ game_state['current_player'] = 'opponent'
195
+
196
+ def update_database(type):
197
+ """Update the game state in the database."""
198
+ game_state = st.session_state.game_state
199
+ gameId = game_state['game_id']
200
+ games.update_one({'game_id': gameId, 'type': type},
201
+ {'$set': {'attacking_board': game_state['player_attacks'],
202
+ 'oponent_moves': game_state['openent_moves']}})
203
+
204
+ def opponent_turn():
205
+ """Process the opponent's turn."""
206
+ game_state = st.session_state.game_state
207
+ with st.spinner("Opponent is making a move..."):
208
+ opponent_doc = games.find_one({'game_id': ObjectId(game_state['game_id']), 'type': 'opponent'})
209
+ print("opponent_doc:", opponent_doc)
210
+ row, col, comment = get_bedrock_claude_move(opponent_doc['attacking_board'], openent_moves=opponent_doc['oponent_moves'])
211
+ game_state['current_player'] = 'player'
212
+
213
+ if game_state['player_board'][row][col] != EMPTY and game_state['opponent_attacks'][row][col] != MISS:
214
+ game_state['openent_moves'].append({'row': row, 'col': col, 'status': 'hit'})
215
+ game_state['opponent_attacks'][row][col] = HIT
216
+ game_state['player_board'][row][col] = HIT
217
+ game_state['player_hits_left'] -= 1
218
+ game_state['message'] = f"Opponent hit your ship! Cell row {row} col {col} Opponent: {comment}"
219
+ else:
220
+ game_state['openent_moves'].append({'row': row, 'col': col, 'status': 'miss'})
221
+ game_state['opponent_attacks'][row][col] = MISS
222
+ game_state['player_board'][row][col] = MISS
223
+ game_state['message'] = f"Opponent missed! Cell row {row} col {col}, Opponent: {comment}"
224
+
225
+ update_database('opponent')
226
+ game_state['current_player'] = 'player'
227
+
228
+ def main():
229
+ """Main function to run the Battleship game."""
230
+ st.title('Battleship Game vs Bedrock 🏴‍☠️')
231
+
232
+
233
+
234
+ initialize_game()
235
+ if st.session_state.game_state['game_over']:
236
+ st.subheader('Game Over! You Lost!' if st.session_state.game_state['player_hits_left'] == 0 else 'You Won!')
237
+ else:
238
+ st.subheader('Your Turn')
239
+
240
+ col1, col3, col2 = st.columns(3)
241
+ with col1:
242
+ st.subheader('Your Board')
243
+ render_board(st.session_state.game_state['player_board'])
244
+
245
+ with col3:
246
+ st.subheader("Opponent Ships")
247
+ for ship in SHIPS:
248
+ my_ships, status = st.columns(2)
249
+ with my_ships:
250
+ st.write(f" {ship['symbol']} {ship['name']}, size: {ship['size']}")
251
+ with status:
252
+ st.checkbox("Sunk", key=f"{ship['name']}_sunk")
253
+
254
+ st.markdown("#### Opponent history")
255
+ container = st.container(height=250)
256
+ for move in st.session_state.game_state['openent_moves']:
257
+ container.write(f"Row: {move['row']} Col: {move['col']} Status: {move['status']}")
258
+
259
+ with col2:
260
+ st.subheader("Opponent's Board")
261
+ render_board(st.session_state.game_state['player_attacks'], is_opponent=True)
262
+
263
+ st.write(st.session_state.game_state['message'])
264
+
265
+ if st.button('Reset Game'):
266
+ st.session_state.clear()
267
+ st.rerun()
268
+
269
+ if st.session_state.game_state['current_player'] == 'opponent':
270
+ opponent_turn()
271
+ if check_game_over():
272
+ st.session_state.game_state['current_player'] = 'game_over'
273
+ else:
274
+ st.rerun()
275
+
276
+ st.header('Debug')
277
+ expander = st.expander("Game State")
278
+ for row in st.session_state.game_state['opponent_board']:
279
+ expander.markdown(row)
280
+
281
+ if st.button("Chat"):
282
+ pop_chat()
283
+
284
+ if __name__ == '__main__':
285
+ main()
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ pymongo
2
+ boto3
3
+ streamlit