mirror of
https://github.com/waku-org/waku-rust-bindings.git
synced 2025-02-19 18:08:34 +00:00
First commit tic-tac-toe
This commit is contained in:
parent
dfe068222d
commit
2ed472ef8d
@ -2,4 +2,4 @@
|
||||
|
||||
members = [
|
||||
"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/)
|
||||
mod general;
|
||||
mod node;
|
||||
pub mod node;
|
||||
pub mod utils;
|
||||
|
||||
// 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 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
|
||||
pub trait WakuNodeState {}
|
||||
|
||||
@ -65,7 +69,7 @@ impl WakuNodeHandle<Initialized> {
|
||||
}
|
||||
}
|
||||
|
||||
impl WakuNodeHandle<Running> {
|
||||
impl<Running: WakuNodeState> WakuNodeHandle<Running> {
|
||||
/// Stops a Waku node
|
||||
/// as per the [specification](https://rfc.vac.dev/spec/36/#extern-char-waku_stop)
|
||||
pub fn stop(self) -> Result<WakuNodeHandle<Initialized>> {
|
||||
@ -95,20 +99,45 @@ impl WakuNodeHandle<Running> {
|
||||
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.
|
||||
/// 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.
|
||||
pub fn relay_publish_message(
|
||||
&self,
|
||||
message: &WakuMessage,
|
||||
pubsub_topic: &String,
|
||||
pubsub_topic: &str,
|
||||
timeout: Option<Duration>,
|
||||
) -> Result<MessageHash> {
|
||||
relay::waku_relay_publish_message(&self.ctx, message, pubsub_topic, timeout)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ pub fn waku_create_content_topic(
|
||||
pub fn waku_relay_publish_message(
|
||||
ctx: &WakuNodeContext,
|
||||
message: &WakuMessage,
|
||||
pubsub_topic: &String,
|
||||
pubsub_topic: &str,
|
||||
timeout: Option<Duration>,
|
||||
) -> Result<MessageHash> {
|
||||
let pubsub_topic = pubsub_topic.to_string();
|
||||
@ -105,7 +105,7 @@ pub fn waku_relay_publish_message(
|
||||
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_ptr = CString::new(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
|
||||
|
||||
let topic = TEST_PUBSUBTOPIC.to_string();
|
||||
let topic = TEST_PUBSUBTOPIC;
|
||||
node1.relay_subscribe(&topic).unwrap();
|
||||
node2.relay_subscribe(&topic).unwrap();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user