This commit is contained in:
nora 2022-11-21 10:24:09 +01:00
parent 6316c6b06e
commit b4badd4290
No known key found for this signature in database
9 changed files with 160 additions and 89 deletions

View file

@ -10,6 +10,3 @@ edition = "2021"
[profile.dev]
opt-level = 3
[profile.release]
debug = true

View file

@ -1,52 +1,6 @@
use std::fmt::{Display, Write};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Player {
X,
O,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum State {
Winner(Player),
InProgress,
Draw,
}
impl Player {
pub fn opponent(self) -> Self {
match self {
Self::X => Self::O,
Self::O => Self::X,
}
}
fn from_u8(num: u8) -> Result<Option<Self>, ()> {
Ok(match num {
0 => Some(Player::X),
1 => Some(Player::O),
2 => None,
_ => return Err(()),
})
}
pub fn as_u8(this: Option<Player>) -> u8 {
match this {
Some(Player::X) => 0,
Some(Player::O) => 1,
None => 2,
}
}
}
impl Display for Player {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::X => "X",
Self::O => "O",
})
}
}
use crate::{Player, State};
#[derive(Clone)]
pub struct Board(u32);
@ -121,10 +75,12 @@ impl Board {
}
mod win_table {
use super::{Board, Player, State};
use super::Board;
use crate::{Player, State};
const WIN_TABLE_SIZE: usize = 2usize.pow(2 * 9);
static WIN_TABLE: &[u8; WIN_TABLE_SIZE] = include_bytes!(concat!(env!("OUT_DIR"), "/win_table"));
static WIN_TABLE: &[u8; WIN_TABLE_SIZE] =
include_bytes!(concat!(env!("OUT_DIR"), "/win_table"));
pub fn result(board: &Board) -> State {
match WIN_TABLE[board.0 as usize] {

View file

@ -1,6 +1,6 @@
use std::ops::ControlFlow;
use std::ops::Index;
use crate::Player;
use crate::{Player, State};
type Position = Option<Player>;
@ -8,32 +8,68 @@ const BOARD_WIDTH: usize = 7;
const BOARD_HEIGHT: usize = 4;
const BOARD_POSITIONS: usize = BOARD_WIDTH * BOARD_HEIGHT;
struct Winner<T>(Option<T>);
impl<T> std::ops::Try for Winner<T> {
// Always None
type Output = Winner<!>;
type Residual = Winner<T>;
fn from_output(_: Self::Output) -> Self {
Self(None)
}
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
match self {
Self(Some(_)) => ControlFlow::Break(self),
Self(None) => ControlFlow::Continue(self),
}
}
}
struct Board {
pub struct Board {
positions: [Position; BOARD_POSITIONS],
}
impl Board {
fn winner() -> Winner<Player> {
todo!()
pub fn new() -> Self {
Self {
positions: [None; BOARD_POSITIONS],
}
}
pub fn result(&self) -> State {
self.check_board()
}
fn check_board(&self) -> State {
self.check_columns()?;
self.check_rows()?;
self.check_diagonals()
}
fn check_columns(&self) -> State {
for i in 0..BOARD_WIDTH {
self.check_four(i, i + BOARD_WIDTH, i + 2 * BOARD_WIDTH, i + 3 * BOARD_WIDTH)?;
}
State::InProgress
}
fn check_rows(&self) -> State {
for row_start in 0..BOARD_HEIGHT {
for offset in 0..4 {
let start = row_start + offset;
self.check_four(start, start + 1, start + 2, start + 3)?;
}
}
State::InProgress
}
fn check_diagonals(&self) -> State {
State::InProgress
}
fn check_four(&self, a: usize, b: usize, c: usize, d: usize) -> State {
self[a]
.map(|player| {
if player == self[a] && player == self[b] && player == self[c] && player == self[d]
{
State::Winner(player)
} else {
State::InProgress
}
})
.unwrap_or(State::InProgress)
}
}
impl Index<usize> for Board {
type Output = Position;
fn index(&self, index: usize) -> &Self::Output {
&self.positions[index]
}
}

View file

@ -1 +1 @@
mod board;
pub mod board;

View file

@ -1,7 +1,4 @@
use crate::{
board::{Player, State},
Board, GamePlayer,
};
use crate::{Board, GamePlayer, Player, State};
impl Board {
pub fn default_play<X: GamePlayer, O: GamePlayer>() -> Option<Player> {

View file

@ -1,14 +1,16 @@
#![feature(never_type, try_trait_v2)]
mod board;
mod connect4;
pub mod connect4;
mod game;
mod perfect;
mod player;
use std::io::Write;
pub use board::{Board, Player, State};
pub use board::Board;
pub use perfect::PerfectPlayer;
pub use player::{Player, State};
pub trait GamePlayer: Default {
fn next_move(&mut self, board: &mut Board, this_player: Player);

View file

@ -9,7 +9,7 @@ fn main() {
let start = SystemTime::now();
for _ in 0..1 {
for _ in 0..100 {
let result = play_round::<PerfectPlayer, GreedyPlayer>(false);
let idx = Player::as_u8(result);
results[idx as usize] += 1;

View file

@ -1,9 +1,6 @@
use std::ops::Neg;
use crate::{
board::{Player, State},
Board, GamePlayer,
};
use crate::{Board, GamePlayer, Player, State};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
enum Score {

86
src/player.rs Normal file
View file

@ -0,0 +1,86 @@
use std::{
fmt::Display,
ops::{ControlFlow, Try},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Player {
X,
O,
}
impl PartialEq<Option<Player>> for Player {
fn eq(&self, other: &Option<Player>) -> bool {
match (self, other) {
(Player::X, Some(Player::X)) => true,
(Player::O, Some(Player::O)) => true,
_ => false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum State {
Winner(Player),
InProgress,
Draw,
}
impl Player {
pub fn opponent(self) -> Self {
match self {
Self::X => Self::O,
Self::O => Self::X,
}
}
pub fn from_u8(num: u8) -> Result<Option<Self>, ()> {
Ok(match num {
0 => Some(Player::X),
1 => Some(Player::O),
2 => None,
_ => return Err(()),
})
}
pub fn as_u8(this: Option<Player>) -> u8 {
match this {
Some(Player::X) => 0,
Some(Player::O) => 1,
None => 2,
}
}
}
impl Display for Player {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::X => "X",
Self::O => "O",
})
}
}
impl std::ops::FromResidual for State {
fn from_residual(residual: <Self as Try>::Residual) -> Self {
residual
}
}
impl Try for State {
// InProgress
type Output = Self;
type Residual = Self;
fn from_output(_: Self::Output) -> Self {
Self::InProgress
}
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
match self {
Self::InProgress => ControlFlow::Continue(self),
Self::Winner(_) | Self::Draw => ControlFlow::Break(self),
}
}
}