Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,6 @@ venv.bak/

# IDE-specific files
.vscode/
.idea/
.idea/

examples/logs
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pip install scripts-of-tribute

## Getting Started
### Creating your bot
To create your own bot, you need to inherit from the `ScriptsOfTribute.base_ai.BaseAI` class and implement the required methods:
To create your own bot, you need to inherit from the `scripts_of_tribute.base_ai.BaseAI` class and implement the required methods:
```python
def pregame_prepare(self):
"""Optional: Prepare your bot before the game starts."""
Expand All @@ -49,7 +49,7 @@ def game_end(self, final_state):
What's important here in the `play` method that bot should return `BasicMove` object from the list, it is because `Move` objects come from the engine with an Identification number `move_id` which is used to quickly identify whether move is legal or not.

### Running the game
The `ScriptsOfTribute.game.Game` class is used to register and run your bots. Here's how to use it:
The `scripts_of_tribute.game.Game` class is used to register and run your bots. Here's how to use it:
```python
from ScriptsOfTribute.game import Game
from Bots.RandomBot import RandomBot
Expand Down Expand Up @@ -136,11 +136,11 @@ This code is available in the `examples` directory, as well with the example bot

## Contributing
if you would like to work with the code locally you might need to (re)generate `protobuf` files.
The library uses gRPC for communication with the C# .NET engine. The `.proto` files are located in the `ScriptsOfTribute/Protos` folder. To generate the necessary Python files, run:
The library uses gRPC for communication with the C# .NET engine. The `.proto` files are located in the `scripts_of_tribute/protos` folder. To generate the necessary Python files, run:
```bash
python -m grpc_tools.protoc -IProtos --python_out=./Protos/ --grpc_python_out=Protos/. Protos/enums.proto Protos/basics.proto Protos/main.proto
python -m grpc_tools.protoc -Iprotos --python_out=./protos/ --grpc_python_out=protos/. protos/enums.proto protos/basics.proto protos/main.proto
```
This will generate the required gRPC Python files in the `Protos` folder.
This will generate the required gRPC Python files in the `protos` folder.

## License
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
Expand Down
33 changes: 29 additions & 4 deletions examples/Bots/MaxPrestigeBot.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import random
import os

from scripts_of_tribute.base_ai import BaseAI
from scripts_of_tribute.board import EndGameState, GameState
from scripts_of_tribute.enums import PlayerEnum, MoveEnum

class MaxPrestigeBot(BaseAI):
Expand All @@ -16,12 +18,14 @@ def select_patron(self, available_patrons):

def play(self, game_state, possible_moves, remaining_time):
best_move = None
best_move_val = -1
best_move_val = -99
if self.start_of_game:
self.player_id = game_state.current_player.player_id
self.start_of_game = False

for first_move in possible_moves:
if first_move.command == MoveEnum.END_TURN:
continue
new_game_state, new_moves = game_state.apply_move(first_move)

if new_game_state.end_game_state is not None: # check if game is over, if we win we are fine with this move
Expand All @@ -40,7 +44,7 @@ def play(self, game_state, possible_moves, remaining_time):
final_game_state, _ = new_game_state.apply_move(second_move)
if final_game_state.end_game_state is not None:
if final_game_state.end_game_state.winner == self.player_id:
return second_move
return first_move
curr_val = final_game_state.current_player.prestige + final_game_state.current_player.power
if curr_val > best_move_val:
best_move = first_move
Expand All @@ -49,5 +53,26 @@ def play(self, game_state, possible_moves, remaining_time):
return next(move for move in possible_moves if move.command == MoveEnum.END_TURN)
return best_move

def game_end(self, final_state):
pass
def game_end(self, end_game_state: EndGameState, final_state: GameState):
# Example how you can log your game for further analysis
log_dir = "logs"
os.makedirs(log_dir, exist_ok=True)

filename = f"game_log_{final_state.state_id}_{end_game_state.winner}.log"
filepath = os.path.join(log_dir, filename)

try:
with open(filepath, "w", encoding="utf-8") as f:
f.write(f"=== Game Ended ===\n")
f.write(f"Winner: {end_game_state.winner}\n")
f.write(f"Reason: {end_game_state.reason}\n")
f.write(f"Context: {end_game_state.AdditionalContext}\n\n")
f.write("=== Completed Actions ===\n")

for action in final_state.completed_actions:
f.write(action + "\n")

print(f"[INFO] Game log saved to: {filepath}")

except Exception as e:
print(f"[ERROR] Failed to save game log: {e}")
2 changes: 1 addition & 1 deletion examples/Bots/RandomBot.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ def play(self, game_state, possible_moves, remaining_time):
pick = random.choice(possible_moves)
return pick

def game_end(self, final_state):
def game_end(self, end_game_state, final_state):
pass
4 changes: 2 additions & 2 deletions scripts_of_tribute/base_ai.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import List

from scripts_of_tribute.board import GameState
from scripts_of_tribute.board import GameState, EndGameState
from scripts_of_tribute.enums import PatronId
from scripts_of_tribute.move import BasicMove

Expand All @@ -17,5 +17,5 @@ def select_patron(self, available_patrons: List[PatronId]):
def play(self, game_state: GameState, possible_moves: List[BasicMove], remaining_time: int) -> BasicMove:
raise NotImplementedError

def game_end(self, final_state):
def game_end(self, end_game_state: EndGameState, final_state: GameState):
pass
1 change: 1 addition & 0 deletions scripts_of_tribute/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class PatronId(Enum):
PELIN = 6
RED_EAGLE = 7
TREASURY = 8
SAINT_ALESSIA = 9

@classmethod
def from_string(cls, patron_str: str) -> 'PatronId':
Expand Down
1 change: 1 addition & 0 deletions scripts_of_tribute/protos/enums.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ enum PatronIdProto {
PELIN = 6;
RED_EAGLE = 7;
TREASURY = 8;
SAINT_ALESSIA = 9;
}

enum MoveEnum
Expand Down
24 changes: 12 additions & 12 deletions scripts_of_tribute/protos/enums_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions scripts_of_tribute/protos/main_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading