game is playable through netcat
This commit is contained in:
parent
14032202a6
commit
016b3aa6cf
5 changed files with 99 additions and 61 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -48,6 +48,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
name = "connect4"
|
name = "connect4"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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';
|
||||||
}
|
|
||||||
loop {
|
println!("Difficulty: {depth}");
|
||||||
match socket.read_i8().await {
|
|
||||||
Ok(read) => {println!("read: {read}")},
|
let mut board = connect4::GameState::new();
|
||||||
Err(_) => {break},
|
|
||||||
|
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 {
|
||||||
|
match socket.read_buf(&mut buffer).await {
|
||||||
|
Ok(0) => return Err(format!("socket disconnected")),
|
||||||
|
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}")),
|
||||||
}
|
}
|
||||||
}
|
}
|
41
src/main.rs
41
src/main.rs
|
@ -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!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue