Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "scripts-of-tribute"
version = "1.0.2"
version = "1.0.3"
authors = [
{ name="Ematerasu", email="tot.thesis.project@gmail.com" },
]
Expand Down
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