Skip to content

Commit

Permalink
Main loop for Python bots, example Python bot
Browse files Browse the repository at this point in the history
  • Loading branch information
nnmm committed Jul 23, 2024
1 parent 5e923ba commit 61b797a
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 6 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,19 @@ See the `--help` text of the judge for more options.
The protocol that the bots use to play consists of JSON requests and responses via standard input/output, with the judge being the client and the bots being the servers.
For instance, the judge will send the bot a JSON message on its stdin asking it to choose up to five cards to play, and the bots will reply via stdout with a JSON message of its own.

You can either
You can either implement this protocol yourself in any language you want, or use one of the existing libraries.

If you use the [Rust](gomori), [Python](gomori-py) or [C#](https://github.com/phwitti/gomori-bot-csharp-template) helper packages, the protocol and game logic is already implemented for you. See their READMEs for more information.
### Option A: Using one of the libraries

### JSON protocol
There are libraries for:

* [Rust](gomori)
* [Python](gomori-py)
* [C#](https://github.com/phwitti/gomori-bot-csharp-template)

They implement the protocol and game logic for you. See their READMEs for more information.

### Option B: Implementing the JSON protocol

To see what the messages look like, you can run the judge with `--log-level trace`.
Messages are newline-delimited, i.e. the JSON must be in a compact format with no newlines, and followed by a newline.
Expand Down
4 changes: 4 additions & 0 deletions bots/schwarzenegger_bot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"nick": "SchwarzeneggerBot",
"cmd": [".venv/bin/python3", "bots/schwarzenegger_bot.py"]
}
21 changes: 21 additions & 0 deletions bots/schwarzenegger_bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from gomori import *
import sys

class SchwarzeneggerBot(Bot):
def new_game(self, color: Color):
pass

def play_first_turn(self, cards: List[Card]) -> Card:
print("I was elected to lead, not to read. Number 3!", file=sys.stderr)
return cards[2]

def play_turn(
self,
cards: List[Card],
board: Board,
cards_won_by_opponent: CardsSet
) -> PlayTurnResponse:
ctp = CardToPlay(card=cards[2], i=0, j=0)
return PlayTurnResponse([ctp])

run_bot(SchwarzeneggerBot())
1 change: 1 addition & 0 deletions gomori-py/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ doc = false
[dependencies]
pyo3 = "0.18.1"
gomori = { path = "../gomori", features = ["python"] }
gomori_bot_utils = { path = "../gomori_bot_utils" }
7 changes: 5 additions & 2 deletions gomori-py/python/gomori/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
from gomori._gomori import *
from typing import List

class Bot():
import json

class Bot:
def new_game(self, color: Color):
raise NotImplementedError()

def play_first_turn(cards: List[Card]) -> Card:
def play_first_turn(self, cards: List[Card]) -> Card:
raise NotImplementedError()

def play_turn(
self,
cards: List[Card],
board: Board,
cards_won_by_opponent: CardsSet
Expand Down
69 changes: 69 additions & 0 deletions gomori-py/src/bot.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use gomori::{Board, Card, CardsSet, Color, Field, PlayTurnResponse};
use gomori_bot_utils::Bot;
use pyo3::{pyfunction, types::PyDict, Py, PyObject, Python};

struct PythonBot {
bot: PyObject,
}

// TODO: Re-evaluate this whole design
impl gomori_bot_utils::Bot for PythonBot {
fn new_game(&mut self, color: Color) {
Python::with_gil(|py| {
let kwargs = PyDict::new(py);
kwargs
.set_item("color", Py::new(py, color).unwrap())
.unwrap();
self.bot
.call_method(py, "new_game", (), Some(kwargs))
.expect("Call to new_game() failed");
})
}

fn play_first_turn(&mut self, cards: [Card; 5]) -> Card {
Python::with_gil(|py| {
let kwargs = PyDict::new(py);
kwargs
.set_item("cards", cards.map(|card| Py::new(py, card).unwrap()))
.unwrap();
self.bot
.call_method(py, "play_first_turn", (), Some(kwargs))
.expect("Call to play_first_turn() failed")
.extract(py)
.expect("play_first_turn() returned wrong type")
})
}

fn play_turn(
&mut self,
cards: [Card; 5],
fields: Vec<Field>,
cards_won_by_opponent: CardsSet,
) -> PlayTurnResponse {
Python::with_gil(|py| {
let kwargs = PyDict::new(py);
kwargs
.set_item("cards", cards.map(|card| Py::new(py, card).unwrap()))
.unwrap();
kwargs
.set_item("board", Py::new(py, Board::new(&fields)).unwrap())
.unwrap();
kwargs
.set_item(
"cards_won_by_opponent",
Py::new(py, cards_won_by_opponent).unwrap(),
)
.unwrap();
self.bot
.call_method(py, "play_turn", (), Some(kwargs))
.expect("Call to play_turn() failed")
.extract(py)
.expect("play_turn() returned wrong type")
})
}
}

#[pyfunction]
pub fn run_bot(bot: PyObject) {
PythonBot { bot }.run().unwrap()
}
3 changes: 3 additions & 0 deletions gomori-py/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use pyo3::prelude::*;

mod bot;

/// A Python module implemented in Rust.
#[pymodule]
#[pyo3(name = "_gomori")]
Expand All @@ -25,5 +27,6 @@ fn gomori(py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<::gomori::PyCalculatedEffects>()?;
m.add_class::<::gomori::Rank>()?;
m.add_class::<::gomori::Suit>()?;
m.add_function(wrap_pyfunction!(bot::run_bot, m)?)?;
Ok(())
}
10 changes: 9 additions & 1 deletion gomori/src/protocol_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ mod python {
impl CardToPlay {
#[new]
#[pyo3(signature = (*, card, i, j, target_field_for_king_ability=None))]
pub(crate) fn py_new(
fn py_new(
card: Card,
i: i8,
j: i8,
Expand All @@ -117,4 +117,12 @@ mod python {
}
}
}

#[pymethods]
impl PlayTurnResponse {
#[new]
fn py_new(cards_to_play: Vec<CardToPlay>) -> Self {
Self(cards_to_play)
}
}
}

0 comments on commit 61b797a

Please sign in to comment.