mirror of
https://github.com/logos-messaging/logos-messaging-rust-bindings.git
synced 2026-01-07 00:13:10 +00:00
First commit tic-tac-toe
This commit is contained in:
parent
dfe068222d
commit
2ed472ef8d
@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
members = [
|
members = [
|
||||||
"basic"
|
"basic"
|
||||||
]
|
, "tic-tac-toe"]
|
||||||
|
|||||||
11
examples/tic-tac-toe/Cargo.toml
Normal file
11
examples/tic-tac-toe/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "tic-tac-toe"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
termcolor = "0.3"
|
||||||
|
waku = { path = "../../waku-bindings", package = "waku-bindings" }
|
||||||
|
serde_json = "1.0"
|
||||||
|
ark-std = "0.4"
|
||||||
|
ctrlc = "3.2.4"
|
||||||
224
examples/tic-tac-toe/src/main.rs
Normal file
224
examples/tic-tac-toe/src/main.rs
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
extern crate termcolor;
|
||||||
|
|
||||||
|
use std::io::Write;
|
||||||
|
use std::str::from_utf8;
|
||||||
|
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
|
||||||
|
|
||||||
|
use waku::{
|
||||||
|
waku_new, Event, WakuNodeConfig,
|
||||||
|
LibwakuResponse, Multiaddr, Running, WakuNodeHandle,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn greeting() {
|
||||||
|
println!(
|
||||||
|
"\nRust TicTacToe\n\
|
||||||
|
--------------\n\
|
||||||
|
A simple game written in the rust programming language.\n\
|
||||||
|
Credits to: https://github.com/flofriday/tictactoe"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_player(player: &char) {
|
||||||
|
let mut stdout = StandardStream::stdout(ColorChoice::Always);
|
||||||
|
|
||||||
|
if player == &'X' {
|
||||||
|
stdout
|
||||||
|
.set_color(ColorSpec::new().set_fg(Some(Color::Blue)))
|
||||||
|
.unwrap();
|
||||||
|
} else if player == &'O' {
|
||||||
|
stdout
|
||||||
|
.set_color(ColorSpec::new().set_fg(Some(Color::Green)))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(&mut stdout, "{}", player).unwrap();
|
||||||
|
stdout.reset().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(board: &[char]) {
|
||||||
|
println!("\n");
|
||||||
|
|
||||||
|
for i in (0..3).rev() {
|
||||||
|
let offset = i * 3;
|
||||||
|
|
||||||
|
print!("-------------\n| ");
|
||||||
|
print_player(&board[offset]);
|
||||||
|
print!(" | ");
|
||||||
|
print_player(&board[offset + 1]);
|
||||||
|
print!(" | ");
|
||||||
|
print_player(&board[offset + 2]);
|
||||||
|
println!(" |");
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("-------------");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ask_user(board: &mut [char], player: char) {
|
||||||
|
loop {
|
||||||
|
print!("Player '");
|
||||||
|
print_player(&player);
|
||||||
|
println!("', enter a number: ");
|
||||||
|
|
||||||
|
let mut input = String::new();
|
||||||
|
if std::io::stdin().read_line(&mut input).is_err() {
|
||||||
|
println!("Couldn't read line! Try again.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(number) = input.trim().parse::<usize>() {
|
||||||
|
if number < 1 || number > 9 {
|
||||||
|
println!("The field number must be between 1 and 9.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let number = number - 1;
|
||||||
|
|
||||||
|
if board[number] == 'X' || board[number] == 'O' {
|
||||||
|
print!("This field is already taken by '");
|
||||||
|
print_player(&board[number]);
|
||||||
|
println!("'.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
board[number] = player;
|
||||||
|
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
println!("Only numbers are allowed.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_won(board: &[char]) -> bool {
|
||||||
|
for tmp in 0..3 {
|
||||||
|
if board[tmp] == board[tmp + 3] && board[tmp] == board[tmp + 6] {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tmp = tmp * 3;
|
||||||
|
|
||||||
|
if board[tmp] == board[tmp + 1] && board[tmp] == board[tmp + 2] {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (board[0] == board[4] && board[0] == board[8])
|
||||||
|
|| (board[2] == board[4] && board[2] == board[6])
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn is_over(board: &[char]) -> bool {
|
||||||
|
board.iter().all(|&v| v == 'X' || v == 'O')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if the game should end.
|
||||||
|
// false otherwise
|
||||||
|
fn game_logic(player: &mut char, board: &mut [char],
|
||||||
|
topic: &str, waku: &WakuNodeHandle<Running>) -> bool {
|
||||||
|
// Ask for user input
|
||||||
|
ask_user(board, *player);
|
||||||
|
|
||||||
|
let board_string: String = board.iter().collect();
|
||||||
|
let board_str_slice: &str = &board_string;
|
||||||
|
|
||||||
|
let _ = waku.relay_publish_txt(topic, &board_str_slice, "tic-tac-toe-example", None);
|
||||||
|
|
||||||
|
// Check if a player won
|
||||||
|
if has_won(&board) {
|
||||||
|
draw(&board);
|
||||||
|
print!("Player '");
|
||||||
|
print_player(&player);
|
||||||
|
println!("' won! \\(^.^)/");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if all fields are used
|
||||||
|
if is_over(&board) {
|
||||||
|
draw(&board);
|
||||||
|
println!("All fields are used. No one won. (._.)");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch player
|
||||||
|
*player = if *player == 'X' { 'O' } else { 'X' };
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut board = ['1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
||||||
|
let mut player = 'X';
|
||||||
|
let topic = "/waku/2/rs/16/64";
|
||||||
|
|
||||||
|
// Create a Waku instance
|
||||||
|
let waku = waku_new(Some(WakuNodeConfig {
|
||||||
|
port: Some(60010),
|
||||||
|
cluster_id: Some(16),
|
||||||
|
..Default::default()
|
||||||
|
}))
|
||||||
|
.expect("should instantiate");
|
||||||
|
|
||||||
|
// ctrlc::set_handler(move ||{
|
||||||
|
// println!("Ctrl+C detected. Exiting gracefully...");
|
||||||
|
// // waku.stop();
|
||||||
|
// }).expect("Error setting Ctrl+C handler");
|
||||||
|
|
||||||
|
let waku = waku.start().expect("waku should start");
|
||||||
|
|
||||||
|
// Establish a closure that handles the incoming messages
|
||||||
|
waku.set_event_callback(&|response| {
|
||||||
|
if let LibwakuResponse::Success(v) = response {
|
||||||
|
let event: Event =
|
||||||
|
serde_json::from_str(v.unwrap().as_str()).expect("Parsing event to succeed");
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::WakuMessage(evt) => {
|
||||||
|
println!("WakuMessage event received: {:?}", evt.waku_message);
|
||||||
|
let message = evt.waku_message;
|
||||||
|
let payload = message.payload.to_vec();
|
||||||
|
match from_utf8(&payload) {
|
||||||
|
Ok(msg) => {
|
||||||
|
println!("::::::::::::::::::::::::::::::::::::::::::::::::::::");
|
||||||
|
println!("Message Received: {}", msg);
|
||||||
|
println!("::::::::::::::::::::::::::::::::::::::::::::::::::::");
|
||||||
|
|
||||||
|
// game_logic(&mut player, &mut board, topic, &waku);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to decode payload as UTF-8: {}", e);
|
||||||
|
// Handle the error as needed, or just log and skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::Unrecognized(err) => panic!("Unrecognized waku event: {:?}", err),
|
||||||
|
_ => panic!("event case not expected"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
waku.relay_subscribe(&topic).expect("waku should subscribe");
|
||||||
|
|
||||||
|
let target_node_multi_addr =
|
||||||
|
"/dns4/store-01.do-ams3.status.staging.status.im/tcp/30303/p2p/16Uiu2HAm3xVDaz6SRJ6kErwC21zBJEZjavVXg7VSkoWzaV1aMA3F"
|
||||||
|
.parse::<Multiaddr>().expect("parse multiaddress");
|
||||||
|
|
||||||
|
waku.connect(&target_node_multi_addr, None)
|
||||||
|
.expect("waku should connect to other node");
|
||||||
|
|
||||||
|
// Welcome the player
|
||||||
|
greeting();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Draw the field
|
||||||
|
draw(&board);
|
||||||
|
|
||||||
|
if game_logic(&mut player, &mut board, topic, &waku) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
examples/tic-tac-toe/src/waku.rs
Normal file
5
examples/tic-tac-toe/src/waku.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
use waku::{
|
||||||
|
waku_destroy, waku_new, Encoding, Event, WakuContentTopic, WakuMessage, WakuNodeConfig, LibwakuResponse,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mut waku: WakuNodeHandle<State: WakuNodeState>;
|
||||||
@ -2,7 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! Implementation on top of [`waku-bindings`](https://rfc.vac.dev/spec/36/)
|
//! Implementation on top of [`waku-bindings`](https://rfc.vac.dev/spec/36/)
|
||||||
mod general;
|
mod general;
|
||||||
mod node;
|
pub mod node;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
// Re-export the LibwakuResponse type to make it accessible outside this module
|
// Re-export the LibwakuResponse type to make it accessible outside this module
|
||||||
|
|||||||
@ -23,6 +23,10 @@ pub use config::WakuNodeConfig;
|
|||||||
pub use events::{Event, WakuMessageEvent};
|
pub use events::{Event, WakuMessageEvent};
|
||||||
pub use relay::waku_create_content_topic;
|
pub use relay::waku_create_content_topic;
|
||||||
|
|
||||||
|
use crate::WakuContentTopic;
|
||||||
|
use crate::Encoding;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
/// Marker trait to disallow undesired waku node states in the handle
|
/// Marker trait to disallow undesired waku node states in the handle
|
||||||
pub trait WakuNodeState {}
|
pub trait WakuNodeState {}
|
||||||
|
|
||||||
@ -65,7 +69,7 @@ impl WakuNodeHandle<Initialized> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WakuNodeHandle<Running> {
|
impl<Running: WakuNodeState> WakuNodeHandle<Running> {
|
||||||
/// Stops a Waku node
|
/// Stops a Waku node
|
||||||
/// as per the [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_stop)
|
/// as per the [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_stop)
|
||||||
pub fn stop(self) -> Result<WakuNodeHandle<Initialized>> {
|
pub fn stop(self) -> Result<WakuNodeHandle<Initialized>> {
|
||||||
@ -95,20 +99,45 @@ impl WakuNodeHandle<Running> {
|
|||||||
peers::waku_connect(&self.ctx, address, timeout)
|
peers::waku_connect(&self.ctx, address, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn relay_publish_txt(
|
||||||
|
&self,
|
||||||
|
pubsub_topic: &str,
|
||||||
|
msg_txt: &str,
|
||||||
|
content_topic_name: &'static str,
|
||||||
|
timeout: Option<Duration>,
|
||||||
|
) -> Result<MessageHash> {
|
||||||
|
let content_topic = WakuContentTopic::new("waku", "2", content_topic_name, Encoding::Proto);
|
||||||
|
let message = WakuMessage::new(
|
||||||
|
msg_txt,
|
||||||
|
content_topic,
|
||||||
|
0,
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_millis()
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
Vec::new(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
relay::waku_relay_publish_message(&self.ctx, &message, pubsub_topic, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
/// Publish a message using Waku Relay.
|
/// Publish a message using Waku Relay.
|
||||||
/// As per the [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_relay_publishchar-messagejson-char-pubsubtopic-int-timeoutms)
|
/// As per the [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_relay_publishchar-messagejson-char-pubsubtopic-int-timeoutms)
|
||||||
/// The pubsub_topic parameter is optional and if not specified it will be derived from the contentTopic.
|
/// The pubsub_topic parameter is optional and if not specified it will be derived from the contentTopic.
|
||||||
pub fn relay_publish_message(
|
pub fn relay_publish_message(
|
||||||
&self,
|
&self,
|
||||||
message: &WakuMessage,
|
message: &WakuMessage,
|
||||||
pubsub_topic: &String,
|
pubsub_topic: &str,
|
||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
) -> Result<MessageHash> {
|
) -> Result<MessageHash> {
|
||||||
relay::waku_relay_publish_message(&self.ctx, message, pubsub_topic, timeout)
|
relay::waku_relay_publish_message(&self.ctx, message, pubsub_topic, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Subscribe to WakuRelay to receive messages matching a content filter.
|
/// Subscribe to WakuRelay to receive messages matching a content filter.
|
||||||
pub fn relay_subscribe(&self, pubsub_topic: &String) -> Result<()> {
|
pub fn relay_subscribe(&self, pubsub_topic: &str) -> Result<()> {
|
||||||
relay::waku_relay_subscribe(&self.ctx, pubsub_topic)
|
relay::waku_relay_subscribe(&self.ctx, pubsub_topic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -60,7 +60,7 @@ pub fn waku_create_content_topic(
|
|||||||
pub fn waku_relay_publish_message(
|
pub fn waku_relay_publish_message(
|
||||||
ctx: &WakuNodeContext,
|
ctx: &WakuNodeContext,
|
||||||
message: &WakuMessage,
|
message: &WakuMessage,
|
||||||
pubsub_topic: &String,
|
pubsub_topic: &str,
|
||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
) -> Result<MessageHash> {
|
) -> Result<MessageHash> {
|
||||||
let pubsub_topic = pubsub_topic.to_string();
|
let pubsub_topic = pubsub_topic.to_string();
|
||||||
@ -105,7 +105,7 @@ pub fn waku_relay_publish_message(
|
|||||||
handle_response(code, result)
|
handle_response(code, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn waku_relay_subscribe(ctx: &WakuNodeContext, pubsub_topic: &String) -> Result<()> {
|
pub fn waku_relay_subscribe(ctx: &WakuNodeContext, pubsub_topic: &str) -> Result<()> {
|
||||||
let pubsub_topic = pubsub_topic.to_string();
|
let pubsub_topic = pubsub_topic.to_string();
|
||||||
let pubsub_topic_ptr = CString::new(pubsub_topic)
|
let pubsub_topic_ptr = CString::new(pubsub_topic)
|
||||||
.expect("CString should build properly from pubsub topic")
|
.expect("CString should build properly from pubsub topic")
|
||||||
|
|||||||
@ -57,7 +57,7 @@ async fn test_echo_messages(
|
|||||||
|
|
||||||
node2.set_event_callback(&closure); // Set the event callback with the closure
|
node2.set_event_callback(&closure); // Set the event callback with the closure
|
||||||
|
|
||||||
let topic = TEST_PUBSUBTOPIC.to_string();
|
let topic = TEST_PUBSUBTOPIC;
|
||||||
node1.relay_subscribe(&topic).unwrap();
|
node1.relay_subscribe(&topic).unwrap();
|
||||||
node2.relay_subscribe(&topic).unwrap();
|
node2.relay_subscribe(&topic).unwrap();
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user