game is playable through netcat

This commit is contained in:
cqql 2024-10-19 12:01:41 +02:00
parent 14032202a6
commit 016b3aa6cf
5 changed files with 99 additions and 61 deletions

1
Cargo.lock generated
View file

@ -48,6 +48,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
name = "connect4" name = "connect4"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bytes",
"tokio", "tokio",
] ]

View file

@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
bytes = "1.7.2"
tokio = { version = "1.38.0", features = [ tokio = { version = "1.38.0", features = [
"rt-multi-thread", "rt-multi-thread",
"macros", "macros",

View file

@ -118,22 +118,25 @@ impl GameState {
return score; return score;
} }
pub fn display(&self) { pub fn to_string(&self) -> String {
let mut string = String::new();
for irow in (0..NROWS).rev() { for irow in (0..NROWS).rev() {
print!("\n "); string += " ";
for icol in 0..NCOLS { for icol in 0..NCOLS {
let idx = irow + 7 * icol; let idx = irow + 7 * icol;
if (self.bitboards[0] >> idx) & 1 != 0 { if (self.bitboards[0] >> idx) & 1 != 0 {
print!(""); string += "";
} else if (self.bitboards[1] >> idx) & 1 != 0 { } else if (self.bitboards[1] >> idx) & 1 != 0 {
print!(""); string += "";
} else { } else {
print!("· "); string += "· ";
} }
} }
string += "\n";
} }
print!("\n 1 2 3 4 5 6 7\n\n"); string += " 1 2 3 4 5 6 7\n\n";
string
} }
} }

View file

@ -1,31 +1,89 @@
use bytes::BytesMut;
use std::f32::consts::E;
use tokio::{ use tokio::{
// AsyncWriteExt trait provides asynchronous write methods like write_all
io::{AsyncReadExt, AsyncWriteExt}, io::{AsyncReadExt, AsyncWriteExt},
net::{TcpListener, TcpStream}, net::TcpStream,
}; };
use crate::connect4::GameState; use crate::connect4;
/// The Server struct holds the tokio TcpListener which listens for
/// incoming TCP connections.
#[derive(Debug)] #[derive(Debug)]
pub struct GameServer { pub struct GameServer {}
}
impl GameServer { impl GameServer {
pub async fn handle(socket: &mut TcpStream, game_state: GameState) { pub async fn handle(socket: &mut TcpStream) -> Result<(), String> {
match socket.write_all("Hello!!".as_bytes()).await { send_text("Connect 4\nChoose your difficulty [1-9]: ", socket).await?;
Ok(_) => {println!("socket socketed successfully")},
Err(e) => {println!("socket didn't socket: {e}")}, let depth = get_one_char(b"123456789", socket).await? - b'0';
println!("Difficulty: {depth}");
let mut board = connect4::GameState::new();
let mut end_score: i64 = -1;
while end_score == -1 {
match board.counter % 2 == 0 {
true => {
send_text(format!("Move {}:\n", board.counter).as_str(), socket).await?;
send_text(board.to_string().as_str(), socket).await?;
send_text("Choose column: ", socket).await?;
let mut player_move = get_one_char(b"1234567", socket).await? - b'0';
while !board.move_is_valid((player_move - 1).into()) {
send_text("Illegal move.\nChoose column: ", socket).await?;
player_move = get_one_char(b"1234567", socket).await? - b'0';
} }
board.make_move((player_move - 1).into());
end_score = board.end_state_reached();
send_text("\n", socket).await?;
},
false => {
let best_move = connect4::get_minimax_move(&mut board, depth.into());
board.make_move(best_move);
end_score = board.end_state_reached();
}
}
}
if end_score == 0 {
send_text( "It's a Tie\n\n", socket).await?;
} else if end_score > 0 {
send_text("White (●) Won!\n\n", socket).await?;
} else {
send_text("Black (○) Won!\n\n", socket).await?;
}
send_text(&board.to_string(), socket).await?;
Ok(())
}
}
async fn get_one_char(allowed: &[u8], socket: &mut TcpStream) -> Result<u8, String> {
let mut buffer = BytesMut::with_capacity(100);
loop { loop {
match socket.read_i8().await { match socket.read_buf(&mut buffer).await {
Ok(read) => {println!("read: {read}")}, Ok(0) => return Err(format!("socket disconnected")),
Err(_) => {break}, Ok(read) => {
println!("read {read} bytes: {:?}", buffer);
for byte in &buffer {
if allowed.contains(byte) {
return Ok(*byte);
}
}
buffer.clear();
}
Err(e) => return Err(format!("socket error while reading a digit: {e}")),
} }
} }
} }
async fn send_text(text: &str, socket: &mut TcpStream) -> Result<(), String> {
match socket.write_all(text.as_bytes()).await {
Ok(_) => Ok(()),
Err(e) => Err(format!("socket error while sending text: {e}")),
}
} }

View file

@ -1,4 +1,3 @@
use connect4::GameState;
use game_server::GameServer; use game_server::GameServer;
use tokio::net::TcpListener; use tokio::net::TcpListener;
@ -7,21 +6,20 @@ mod game_server;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let addr = format!("127.0.0.1:{}", 6379); let addr = format!("127.0.0.1:{}", 1234);
let listener = match TcpListener::bind(&addr).await { let listener = TcpListener::bind(&addr)
Ok(tcp_listener) => { .await
println!("TCP listener started on port 6379"); .expect(format!("Could not bind the TCP listener to {}", &addr).as_str());
tcp_listener
}
Err(e) => panic!("Could not bind the TCP listener to {}. Err: {}", &addr, e),
};
loop { loop {
match listener.accept().await { match listener.accept().await {
Ok((mut socket, _)) => { Ok((mut socket, _)) => {
tokio::spawn(async move { tokio::spawn(async move {
GameServer::handle(&mut socket, GameState::new()).await; match GameServer::handle(&mut socket).await {
Ok(_) => {println!("Game finished")},
Err(e) => {println!("Game failed with error: {e}")},
};
}); });
} }
Err(e) => { Err(e) => {
@ -30,28 +28,5 @@ async fn main() {
} }
} }
let mut board = connect4::GameState::new();
let depth = 7;
let mut end_score: i64 = -1;
while end_score == -1 {
println!("Move {}:", board.counter);
board.display();
let best_move = connect4::get_minimax_move(&mut board, depth);
board.make_move(best_move);
end_score = board.end_state_reached();
}
println!("Move {}:", board.counter);
board.display();
if end_score == 0 {
println!("It's a Tie");
} else if end_score > 0 {
println!("White (●) Won!");
} else {
println!("Black (○) Won!");
}
} }