Merge pull request #3 from LinlyBoi/raylib-time

Raylib time
This commit is contained in:
Linly
2023-05-07 00:01:05 +03:00
committed by GitHub
10 changed files with 543 additions and 223 deletions

BIN
resouces/board.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
resouces/bracc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

View File

@@ -1,2 +1,124 @@
use raylib::{
prelude::{Color, MouseButton},
RaylibHandle,
};
use crate::gamedata::{algorithms::minimax_decision, Board, Disk};
pub const STARTY: i32 = 9;
pub const STARTX: i32 = 7;
const WX: i32 = 14;
const WY: i32 = 14;
const CIRCLEWIDTH: i32 = 56;
#[cfg(test)]
mod tests;
#[derive(Clone)]
pub struct PlayState {
pub circles: Vec<(i32, i32, Disk)>,
pub bottom: Vec<i32>,
pub player_turn: bool,
pub board: Board,
}
impl Default for PlayState {
fn default() -> Self {
Self {
circles: vec![],
bottom: vec![6, 6, 6, 6, 6, 6, 6],
player_turn: true,
board: Board::default(),
}
}
}
impl PlayState {
pub fn play_human(&mut self, column: i32) {
self.board.play(Disk::P1, column as usize);
self.bottom[column as usize] -= 1;
let (x, y) = get_circle_coords(column, self.bottom[column as usize]);
self.circles.push((x, y, Disk::P1));
self.player_turn = false;
}
pub fn play_cpu(&mut self, diff: &i32, cook: fn(&Board, Disk, &i32) -> Board) {
self.board.play(
Disk::P2,
cook(&self.board, Disk::P2, diff).last_move as usize,
);
let column: i32 = self.board.last_move;
self.bottom[column as usize] -= 1;
let (x, y) = get_circle_coords(column, self.bottom[column as usize]);
self.circles.push((x, y, Disk::P2));
self.player_turn = false;
self.player_turn = true;
}
}
#[derive(Clone)]
pub struct MenuState {
pub difficulty: i32,
p1: (Color, Disk),
p2: (Color, Disk),
pub selection: Selection,
pub strategy: Strategy,
}
#[derive(Clone)]
pub enum Strategy {
MiniMax,
AlphaBeta,
}
impl Default for MenuState {
fn default() -> Self {
Self {
difficulty: 3,
p1: (Color::RED, Disk::P1),
p2: (Color::YELLOW, Disk::P2),
selection: Selection::Difficulty,
strategy: Strategy::MiniMax,
}
}
}
impl MenuState {
pub fn init(&mut self, rl: &RaylibHandle) -> bool {
dbg!(&self.selection);
match self.selection {
Selection::Difficulty => {
if rl.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON) {
self.difficulty = 3;
self.selection = Selection::Cooking;
}
if rl.is_mouse_button_pressed(MouseButton::MOUSE_RIGHT_BUTTON) {
self.difficulty = 5;
self.selection = Selection::Cooking;
}
}
Selection::Cooking => {
if rl.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON) {
self.difficulty = 3;
self.selection = Selection::Done;
}
if rl.is_mouse_button_pressed(MouseButton::MOUSE_RIGHT_BUTTON) {
self.difficulty = 5;
self.selection = Selection::Done;
}
}
Selection::Done => return true,
}
false
}
}
#[derive(Clone)]
pub enum GameState {
Play(PlayState),
MainMenu(MenuState),
}
#[derive(Clone, Debug)]
pub enum Selection {
Difficulty,
Cooking,
Done,
}
fn get_circle_coords(x: i32, y: i32) -> (i32, i32) {
let mut returned: (i32, i32) = (0, 0);
returned.0 = STARTY + (CIRCLEWIDTH * x) + (WX * x);
returned.1 = STARTX + (CIRCLEWIDTH * y) + (WY * y);
returned
}

View File

@@ -8,7 +8,7 @@ pub fn minimax_decision(board: &Board, disk: Disk, depth: &i32) -> Board {
}
fn maximise(board: &Board, disk: &Disk, depth: &i32) -> (Option<Board>, i32) {
match board.game_over() || *depth == 0 {
true => return (None, get_score(board, *disk)),
true => (None, get_score(board, *disk)),
false => {
let (mut max_child, mut max_utility): (Option<Board>, i32) = (None, i32::MIN);
for child in board.get_children(*disk) {
@@ -23,7 +23,7 @@ fn maximise(board: &Board, disk: &Disk, depth: &i32) -> (Option<Board>, i32) {
}
fn minimise(board: &Board, disk: &Disk, depth: &i32) -> (Option<Board>, i32) {
match board.game_over() || *depth == 0 {
true => return (None, get_score(board, flip_disk(*disk))),
true => (None, get_score(board, flip_disk(*disk))),
false => {
let (mut min_child, mut min_utility): (Option<Board>, i32) = (None, i32::MAX);
for child in board.get_children(*disk) {
@@ -36,20 +36,86 @@ fn minimise(board: &Board, disk: &Disk, depth: &i32) -> (Option<Board>, i32) {
}
}
}
pub fn minimax_decision_pruning(board: &Board, disk: Disk, depth: &i32) -> Board {
let (child, _) = maximise_pruning(board, &disk, depth, i32::MIN, i32::MAX);
match child {
Some(state) => state,
None => Board::default(),
}
}
fn maximise_pruning(
board: &Board,
disk: &Disk,
depth: &i32,
alpha: i32,
mut beta: i32,
) -> (Option<Board>, i32) {
match board.game_over() || *depth == 0 {
true => (None, get_score(board, *disk)),
false => {
let (mut max_child, mut max_utility): (Option<Board>, i32) = (None, i32::MIN);
for child in board.get_children(*disk) {
let (_, utility) =
minimise_pruning(&child, &flip_disk(*disk), &(depth - 1), alpha, beta);
if utility <= alpha {
break;
}
if utility < beta {
beta = utility;
}
if utility > max_utility {
(max_child, max_utility) = (Some(child), utility)
}
}
(max_child, max_utility)
}
}
}
fn minimise_pruning(
board: &Board,
disk: &Disk,
depth: &i32,
mut alpha: i32,
beta: i32,
) -> (Option<Board>, i32) {
match board.game_over() || *depth == 0 {
true => (None, get_score(board, flip_disk(*disk))),
false => {
let (mut min_child, mut min_utility): (Option<Board>, i32) = (None, i32::MAX);
for child in board.get_children(*disk) {
let (_, utility) =
maximise_pruning(&child, &flip_disk(*disk), &(depth - 1), alpha, beta);
if utility >= beta {
break;
}
if utility > alpha {
alpha = utility;
}
if utility < min_utility {
(min_child, min_utility) = (Some(child), utility)
}
}
(min_child, min_utility)
}
}
}
#[test]
fn minimax_test() {
let mut board = Board::default();
let mut disk = Disk::BLU;
let depth = 5;
let turn1 = board.play(disk, minimax_decision(&board, disk, &5).last_move);
let mut disk = Disk::P2;
let _depth = 5;
let _turn1 = board.play(disk, minimax_decision(&board, disk, &5).last_move as usize);
dbg!(&board.columns);
disk = flip_disk(disk);
let turn2 = board.play(disk, minimax_decision(&board, disk, &5).last_move);
let _turn2 = board.play(disk, minimax_decision(&board, disk, &5).last_move as usize);
disk = flip_disk(disk);
let turn3 = board.play(disk, minimax_decision(&board, disk, &5).last_move);
let _turn3 = board.play(disk, minimax_decision(&board, disk, &5).last_move as usize);
disk = flip_disk(disk);
let turn4 = board.play(disk, minimax_decision(&board, disk, &5).last_move);
let _turn4 = board.play(disk, minimax_decision(&board, disk, &5).last_move as usize);
disk = flip_disk(disk);
let turn5 = board.play(disk, minimax_decision(&board, disk, &5).last_move);
let _turn5 = board.play(disk, minimax_decision(&board, disk, &5).last_move as usize);
for column in board.columns.as_rows() {
column
.iter()
@@ -58,7 +124,49 @@ fn minimax_test() {
x
})
.count();
println!("");
println!();
}
assert!(false);
}
#[test]
fn minimax_pruning_test() {
let mut board = Board::default();
let mut disk = Disk::P2;
let _depth = 5;
let _turn1 = board.play(
disk,
minimax_decision_pruning(&board, disk, &5).last_move as usize,
);
dbg!(&board.columns);
disk = flip_disk(disk);
let _turn2 = board.play(
disk,
minimax_decision_pruning(&board, disk, &5).last_move as usize,
);
disk = flip_disk(disk);
let _turn3 = board.play(
disk,
minimax_decision_pruning(&board, disk, &5).last_move as usize,
);
disk = flip_disk(disk);
let _turn4 = board.play(
disk,
minimax_decision_pruning(&board, disk, &5).last_move as usize,
);
disk = flip_disk(disk);
let _turn5 = board.play(
disk,
minimax_decision_pruning(&board, disk, &5).last_move as usize,
);
for column in board.columns.as_rows() {
column
.iter()
.map(|x| {
print!("{:#?},", x);
x
})
.count();
println!();
}
assert!(false);
}

View File

@@ -6,25 +6,24 @@ use super::{
Board, Disk,
};
//multipliers
const POT_STREAK: i32 = 4; //one streak is kind of poopy
const POT_STREAKS: i32 = 6;
const POT_STREAK: i32 = 3; //one streak is kind of poopy
const POT_STREAKS: i32 = 4;
const POT_WIN: i32 = 5; // should be nerfed if its just 1 potential win
const POT_WINS: i32 = 8;
const SCORE_DIFF: i32 = 6;
const MAX_WINS: i32 = 17;
const POT_WINS: i32 = 6;
const SCORE_DIFF: i32 = 40;
pub fn get_score(board: &Board, disk: Disk) -> i32 {
//this should be summing up a bunch of functions defined below this one
let sequences = get_streaks(&board.columns, &disk);
let score: i32 = match disk {
Disk::RED => board.red_score - board.blu_score,
Disk::BLU => board.blu_score - board.red_score,
Disk::P1 => board.p1_score - board.p2_score,
Disk::P2 => board.p2_score - board.p1_score,
Disk::EMPTY => panic!("Why would you ever"),
};
potential_streaks(&sequences, &disk) + potential_wins(&sequences, &disk) + score * SCORE_DIFF
}
fn potential_wins(sequences: &Vec<Vec<Disk>>, _disk: &Disk) -> i32 {
let count: i32 = sequences.iter().count() as i32;
let count: i32 = sequences.len() as i32;
// for win in sequences {
// if win
// .iter()
@@ -70,7 +69,7 @@ fn get_streaks(board: &Array2D<Disk>, player_disk: &Disk) -> Vec<Vec<Disk>> {
for index in empty_indices {
let moves = get_legal_moves(&index, (board.num_rows(), board.num_columns()));
for direction in moves {
let mut sequence = heur_scan(&board, &index, direction.clone(), 4, *player_disk);
let sequence = heur_scan(board, &index, direction.clone(), 4, *player_disk);
//dbg!(&index, &direction, &poopy);
match sequence
.iter()
@@ -95,7 +94,7 @@ fn get_wins(board: &Array2D<Disk>, player_disk: &Disk) -> Vec<Vec<Disk>> {
for direction in moves {
let mut win: Vec<Disk> = Vec::with_capacity(4);
win.append(&mut heur_scan(
&board,
board,
&index,
direction.clone(),
4,
@@ -115,7 +114,7 @@ fn get_wins(board: &Array2D<Disk>, player_disk: &Disk) -> Vec<Vec<Disk>> {
}
let opp_direction = flip_direction(direction.clone());
let mut opp_sequence = heur_scan(
&board,
board,
&index,
opp_direction,
(4 - win.len() + 1) as i32,
@@ -157,31 +156,31 @@ fn heur_scan(
// dbg!(current_index);
//go to next element
match direction {
Direction::DOWN => {
Direction::Down => {
if current_index.0 == 0 {
break;
}
current_index = dec_row(&current_index, 1);
}
Direction::UP => {
Direction::Up => {
if current_index.0 == board.num_rows() - 1 {
break;
}
current_index = inc_row(&current_index, 1);
}
Direction::LEFT => {
Direction::Left => {
if current_index.1 == 0 {
break;
}
current_index = dec_col(&current_index, 1);
}
Direction::RIGHT => {
Direction::Right => {
if current_index.1 == board.num_columns() - 1 {
break;
}
current_index = inc_col(&current_index, 1);
}
Direction::UPRIGHT => {
Direction::UpRight => {
if current_index.0 == board.num_rows() - 1
|| current_index.1 == board.num_columns() - 1
{
@@ -189,20 +188,20 @@ fn heur_scan(
}
current_index = inc_both(&current_index, 1);
}
Direction::UPLEFT => {
Direction::UpLeft => {
if current_index.0 == board.num_columns() - 1 || current_index.1 == 0 {
break;
}
current_index = inc_dec(&current_index, 1);
}
Direction::DOWNRIGHT => {
Direction::DownRight => {
if current_index.0 == 0 || current_index.1 == board.num_columns() - 1 {
break;
}
current_index = dec_inc(&current_index, 1);
}
Direction::DOWNLEFT => {
Direction::DownLeft => {
if current_index.0 == 0 || current_index.1 == 0 {
break;
}
@@ -225,44 +224,44 @@ fn heur_scan(
#[test]
fn streak_test_1() {
let mut board = Board::default();
board.play(Disk::BLU, 3);
board.play(Disk::BLU, 3);
board.play(Disk::BLU, 3);
board.play(Disk::BLU, 2);
board.play(Disk::BLU, 1);
let sequences = get_streaks(&board.columns, &Disk::BLU);
assert_eq!(18, potential_streaks(&sequences, &Disk::BLU));
board.play(Disk::BLU, 0);
let sequences = get_streaks(&board.columns, &Disk::BLU);
board.play(Disk::BLU, 3);
board.play(Disk::BLU, 3);
board.play(Disk::BLU, 3);
board.play(Disk::BLU, 3);
let sequences = get_streaks(&board.columns, &Disk::BLU);
assert_eq!(12, potential_streaks(&sequences, &Disk::BLU));
board.play(Disk::P2, 3);
board.play(Disk::P2, 3);
board.play(Disk::P2, 3);
board.play(Disk::P2, 2);
board.play(Disk::P2, 1);
let sequences = get_streaks(&board.columns, &Disk::P2);
assert_eq!(POT_STREAKS * 3, potential_streaks(&sequences, &Disk::P2));
board.play(Disk::P2, 0);
let _sequences = get_streaks(&board.columns, &Disk::P2);
board.play(Disk::P2, 3);
board.play(Disk::P2, 3);
board.play(Disk::P2, 3);
board.play(Disk::P2, 3);
let sequences = get_streaks(&board.columns, &Disk::P2);
assert_eq!(POT_STREAKS * 2, potential_streaks(&sequences, &Disk::P2));
}
#[test]
fn win_test_flipping() {
let mut board = Board::default();
board.play(Disk::BLU, 3);
board.play(Disk::RED, 4);
board.play(Disk::BLU, 4);
board.play(Disk::RED, 5);
board.play(Disk::RED, 5);
board.play(Disk::RED, 6);
board.play(Disk::RED, 6);
board.play(Disk::RED, 6);
board.play(Disk::BLU, 6);
let sequences = get_wins(&board.columns, &Disk::BLU);
board.play(Disk::P2, 3);
board.play(Disk::P1, 4);
board.play(Disk::P2, 4);
board.play(Disk::P1, 5);
board.play(Disk::P1, 5);
board.play(Disk::P1, 6);
board.play(Disk::P1, 6);
board.play(Disk::P1, 6);
board.play(Disk::P2, 6);
let sequences = get_wins(&board.columns, &Disk::P2);
dbg!(&sequences);
assert_eq!(POT_WIN, potential_wins(&sequences, &Disk::BLU));
assert_eq!(POT_WIN, potential_wins(&sequences, &Disk::P2));
}
#[test]
fn win_test_flipping_hard() {
let mut board = Board::default();
board.play(Disk::BLU, 1);
board.play(Disk::BLU, 2);
board.play(Disk::BLU, 4);
let sequences = get_wins(&board.columns, &Disk::BLU);
assert_eq!(POT_WIN, potential_wins(&sequences, &Disk::BLU));
board.play(Disk::P2, 1);
board.play(Disk::P2, 2);
board.play(Disk::P2, 4);
let sequences = get_wins(&board.columns, &Disk::P2);
assert_eq!(POT_WIN, potential_wins(&sequences, &Disk::P2));
}

View File

@@ -1,15 +1,15 @@
pub fn inc_row((row, col): &(usize, usize), value: usize) -> (usize, usize) {
(row + value as usize, *col)
(row + value, *col)
}
pub fn inc_col((row, col): &(usize, usize), value: usize) -> (usize, usize) {
(*row, col + value as usize)
(*row, col + value)
}
pub fn dec_row((row, col): &(usize, usize), value: usize) -> (usize, usize) {
(row - value as usize, *col)
(row - value, *col)
}
pub fn dec_col((row, col): &(usize, usize), value: usize) -> (usize, usize) {
(*row, col - value as usize)
(*row, col - value)
}
pub fn inc_both((row, col): &(usize, usize), value: usize) -> (usize, usize) {
(row + value, col + value)

View File

@@ -1,4 +1,4 @@
mod algorithms;
pub mod algorithms;
mod heuristic;
mod indices;
mod score_checkers;
@@ -10,10 +10,10 @@ pub use indices::*;
#[derive(Clone)]
pub struct Board {
red_score: i32,
blu_score: i32,
p1_score: i32,
p2_score: i32,
columns: Array2D<Disk>,
last_move: usize,
pub last_move: i32,
}
impl Default for Board {
@@ -21,8 +21,8 @@ impl Default for Board {
let columns = Array2D::filled_with(Disk::EMPTY, 6, 7);
Self {
red_score: 0,
blu_score: 0,
p1_score: 0,
p2_score: 0,
columns,
last_move: 0,
}
@@ -30,18 +30,19 @@ impl Default for Board {
}
impl Board {
fn getscore(&self) -> (i32, i32) {
(self.red_score, self.blu_score)
pub fn getscore(&self) -> (i32, i32) {
(self.p1_score, self.p2_score)
}
fn play(&mut self, disk: Disk, col: usize) -> bool {
let column = &self.columns.as_columns()[col as usize];
pub fn play(&mut self, disk: Disk, col: usize) -> bool {
let column = &self.columns.as_columns()[col];
let empty = column.iter().filter(|&a| matches!(a, Disk::EMPTY)).count();
// dbg!(empty);
let top = column.len() - empty;
match self.columns.set(top, col as usize, disk) {
match self.columns.set(top, col, disk) {
Ok(_) => {
self.score_check((top, col));
self.last_move = col;
self.last_move = col as i32;
//dbg!(self.p1_score, self.p2_score);
true
}
Err(_) => false,
@@ -55,7 +56,7 @@ impl Board {
);
match self.columns.get(index.0, index.1) {
Some(disk) => match disk {
Disk::RED => {
Disk::P1 => {
for _move in moves {
let mut consecutive = scan(&self.columns, &index, _move.clone(), 4);
if consecutive < 4 {
@@ -67,11 +68,11 @@ impl Board {
)
}
if consecutive - 1 == 4 {
self.red_score += 1
self.p1_score += 1
}
}
}
Disk::BLU => {
Disk::P2 => {
for _move in moves {
let mut consecutive = scan(&self.columns, &index, _move.clone(), 4);
if consecutive < 4 {
@@ -83,16 +84,16 @@ impl Board {
)
}
if consecutive - 1 == 4 {
self.blu_score += 1
self.p2_score += 1
}
}
}
Disk::EMPTY => return,
Disk::EMPTY => (),
},
None => return,
None => (),
}
}
fn game_over(&self) -> bool {
pub fn game_over(&self) -> bool {
self.columns
.as_row_major()
.iter()
@@ -115,14 +116,14 @@ impl Board {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Disk {
RED,
BLU,
P1,
P2,
EMPTY,
}
pub fn flip_disk(disk: Disk) -> Disk {
match disk {
Disk::RED => Disk::BLU,
Disk::BLU => Disk::RED,
Disk::P1 => Disk::P2,
Disk::P2 => Disk::P1,
Disk::EMPTY => Disk::EMPTY, //why..just why
}
}

View File

@@ -10,9 +10,8 @@ pub fn scan(
direction: Direction,
depth: i32,
) -> i32 {
let current_disk: &Disk;
match board.get(index.0, index.1) {
Some(disk) => current_disk = disk,
let current_disk: &Disk = match board.get(index.0, index.1) {
Some(disk) => disk,
None => return 0,
};
let mut current_index = *index;
@@ -28,33 +27,33 @@ pub fn scan(
// dbg!(current_index);
//go to next element
match direction {
Direction::DOWN => {
Direction::Down => {
if current_index.0 == 0 {
break;
}
current_index = dec_row(&current_index, 1);
}
Direction::UP => {
Direction::Up => {
if current_index.0 == board.num_rows() - 1 {
break;
}
current_index = inc_row(&current_index, 1);
}
Direction::LEFT => {
Direction::Left => {
if current_index.1 == 0 {
break;
}
current_index = dec_col(&current_index, 1);
// current_index.1 -= 1;
}
Direction::RIGHT => {
Direction::Right => {
if current_index.1 == board.num_columns() - 1 {
break;
}
current_index = inc_col(&current_index, 1);
// current_index.1 += 1;
}
Direction::UPRIGHT => {
Direction::UpRight => {
if current_index.0 == board.num_rows() - 1
|| current_index.1 == board.num_columns() - 1
{
@@ -64,7 +63,7 @@ pub fn scan(
// current_index.1 += 1;
// current_index.0 += 1;
}
Direction::UPLEFT => {
Direction::UpLeft => {
if current_index.0 == board.num_columns() - 1 || current_index.1 == 0 {
break;
}
@@ -73,7 +72,7 @@ pub fn scan(
// current_index.0 += 1;
current_index = inc_dec(&current_index, 1);
}
Direction::DOWNRIGHT => {
Direction::DownRight => {
if current_index.0 == 0 || current_index.1 == board.num_columns() - 1 {
break;
}
@@ -81,7 +80,7 @@ pub fn scan(
// current_index.1 += 1;
// current_index.0 -= 1;
}
Direction::DOWNLEFT => {
Direction::DownLeft => {
if current_index.0 == 0 || current_index.1 == 0 {
break;
}
@@ -108,46 +107,46 @@ pub fn get_legal_moves(
let _max_row = ncol - 1;
let mut moves: Vec<Direction> = vec![];
match *col {
0 => moves.push(Direction::UP),
_max_row => moves.push(Direction::DOWN),
0 => moves.push(Direction::Up),
_max_row => moves.push(Direction::Down),
_ => {
moves.push(Direction::UP);
moves.push(Direction::DOWN);
moves.push(Direction::Up);
moves.push(Direction::Down);
}
};
match *row {
0 => moves.push(Direction::RIGHT),
_max_row => moves.push(Direction::LEFT),
0 => moves.push(Direction::Right),
_max_row => moves.push(Direction::Left),
_ => {
moves.push(Direction::LEFT);
moves.push(Direction::RIGHT)
moves.push(Direction::Left);
moves.push(Direction::Right)
}
};
if moves.contains(&Direction::UP) && moves.contains(&Direction::LEFT) {
moves.push(Direction::UPLEFT);
if moves.contains(&Direction::Up) && moves.contains(&Direction::Left) {
moves.push(Direction::UpLeft);
}
if moves.contains(&Direction::UP) && moves.contains(&Direction::RIGHT) {
moves.push(Direction::UPRIGHT);
if moves.contains(&Direction::Up) && moves.contains(&Direction::Right) {
moves.push(Direction::UpRight);
}
if moves.contains(&Direction::DOWN) && moves.contains(&Direction::LEFT) {
moves.push(Direction::DOWNLEFT);
if moves.contains(&Direction::Down) && moves.contains(&Direction::Left) {
moves.push(Direction::DownLeft);
}
if moves.contains(&Direction::DOWN) && moves.contains(&Direction::RIGHT) {
moves.push(Direction::DOWNRIGHT);
if moves.contains(&Direction::Down) && moves.contains(&Direction::Right) {
moves.push(Direction::DownRight);
}
moves
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Direction {
UP,
DOWN,
LEFT,
RIGHT,
UPLEFT,
UPRIGHT,
DOWNLEFT,
DOWNRIGHT,
Up,
Down,
Left,
Right,
UpLeft,
UpRight,
DownLeft,
DownRight,
}
// serves nothing except do what matches!() should have done all along
// matches works too I'm just dumb
@@ -156,13 +155,13 @@ pub fn variant_eq<T>(a: &T, b: &T) -> bool {
}
pub fn flip_direction(direction: Direction) -> Direction {
match direction {
Direction::UP => Direction::DOWN,
Direction::DOWN => Direction::UP,
Direction::LEFT => Direction::RIGHT,
Direction::RIGHT => Direction::LEFT,
Direction::UPLEFT => Direction::DOWNRIGHT,
Direction::UPRIGHT => Direction::DOWNLEFT,
Direction::DOWNLEFT => Direction::UPRIGHT,
Direction::DOWNRIGHT => Direction::UPLEFT,
Direction::Up => Direction::Down,
Direction::Down => Direction::Up,
Direction::Left => Direction::Right,
Direction::Right => Direction::Left,
Direction::UpLeft => Direction::DownRight,
Direction::UpRight => Direction::DownLeft,
Direction::DownLeft => Direction::UpRight,
Direction::DownRight => Direction::UpLeft,
}
}

View File

@@ -11,128 +11,128 @@ use super::*;
#[test]
fn play() {
let mut board = Board::default();
assert!(board.play(Disk::BLU, 0));
assert!(board.play(Disk::BLU, 0));
assert!(board.play(Disk::BLU, 0));
assert!(board.play(Disk::BLU, 0));
assert_eq!(1, board.blu_score);
assert!(board.play(Disk::BLU, 0));
assert!(board.play(Disk::BLU, 0));
assert!(!board.play(Disk::BLU, 0));
assert!(board.play(Disk::P2, 0));
assert!(board.play(Disk::P2, 0));
assert!(board.play(Disk::P2, 0));
assert!(board.play(Disk::P2, 0));
assert_eq!(1, board.p2_score);
assert!(board.play(Disk::P2, 0));
assert!(board.play(Disk::P2, 0));
assert!(!board.play(Disk::P2, 0));
assert!(board.play(Disk::BLU, 1));
assert!(board.play(Disk::BLU, 1));
assert!(board.play(Disk::BLU, 1));
assert!(board.play(Disk::BLU, 1));
assert!(board.play(Disk::BLU, 1));
assert!(board.play(Disk::BLU, 1));
assert!(!board.play(Disk::BLU, 1));
assert!(board.play(Disk::P2, 1));
assert!(board.play(Disk::P2, 1));
assert!(board.play(Disk::P2, 1));
assert!(board.play(Disk::P2, 1));
assert!(board.play(Disk::P2, 1));
assert!(board.play(Disk::P2, 1));
assert!(!board.play(Disk::P2, 1));
assert!(board.play(Disk::BLU, 2));
assert!(board.play(Disk::BLU, 2));
assert!(board.play(Disk::BLU, 2));
assert!(board.play(Disk::BLU, 2));
assert!(board.play(Disk::BLU, 2));
assert!(board.play(Disk::BLU, 2));
assert!(!board.play(Disk::BLU, 2));
assert!(board.play(Disk::P2, 2));
assert!(board.play(Disk::P2, 2));
assert!(board.play(Disk::P2, 2));
assert!(board.play(Disk::P2, 2));
assert!(board.play(Disk::P2, 2));
assert!(board.play(Disk::P2, 2));
assert!(!board.play(Disk::P2, 2));
assert!(board.play(Disk::BLU, 3));
assert!(board.play(Disk::BLU, 3));
assert!(board.play(Disk::BLU, 3));
assert!(board.play(Disk::BLU, 3));
assert!(board.play(Disk::BLU, 3));
assert!(board.play(Disk::BLU, 3));
assert!(!board.play(Disk::BLU, 3));
assert!(board.play(Disk::P2, 3));
assert!(board.play(Disk::P2, 3));
assert!(board.play(Disk::P2, 3));
assert!(board.play(Disk::P2, 3));
assert!(board.play(Disk::P2, 3));
assert!(board.play(Disk::P2, 3));
assert!(!board.play(Disk::P2, 3));
}
#[test]
fn scan_updown() {
let mut board = Board::default();
board.play(Disk::BLU, 0);
board.play(Disk::BLU, 0);
board.play(Disk::BLU, 0);
board.play(Disk::BLU, 0);
board.play(Disk::BLU, 0);
board.play(Disk::BLU, 0);
board.play(Disk::BLU, 0);
assert_eq!(4, scan(&board.columns, &(4, 0), Direction::DOWN, 4));
assert_eq!(4, scan(&board.columns, &(3, 0), Direction::DOWN, 4));
board.play(Disk::P2, 0);
board.play(Disk::P2, 0);
board.play(Disk::P2, 0);
board.play(Disk::P2, 0);
board.play(Disk::P2, 0);
board.play(Disk::P2, 0);
board.play(Disk::P2, 0);
assert_eq!(4, scan(&board.columns, &(4, 0), Direction::Down, 4));
assert_eq!(4, scan(&board.columns, &(3, 0), Direction::Down, 4));
}
#[test]
fn scan_updown2() {
let mut board = Board::default();
board.play(Disk::BLU, 0);
board.play(Disk::RED, 0);
board.play(Disk::BLU, 0);
board.play(Disk::BLU, 0);
assert_eq!(1, scan(&board.columns, &(0, 0), Direction::UP, 4));
board.play(Disk::P2, 0);
board.play(Disk::P1, 0);
board.play(Disk::P2, 0);
board.play(Disk::P2, 0);
assert_eq!(1, scan(&board.columns, &(0, 0), Direction::Up, 4));
}
#[test]
fn scan_forwardback() {
let mut board = Board::default();
board.play(Disk::BLU, 0);
board.play(Disk::BLU, 1);
board.play(Disk::BLU, 2);
board.play(Disk::BLU, 3);
board.play(Disk::P2, 0);
board.play(Disk::P2, 1);
board.play(Disk::P2, 2);
board.play(Disk::P2, 3);
assert_eq!(4, scan(&board.columns, &(0, 0), Direction::RIGHT, 4));
assert_eq!(4, scan(&board.columns, &(0, 3), Direction::LEFT, 4));
assert_eq!(4, scan(&board.columns, &(0, 0), Direction::Right, 4));
assert_eq!(4, scan(&board.columns, &(0, 3), Direction::Left, 4));
}
#[test]
fn scan_forwardback2() {
let mut board = Board::default();
board.play(Disk::BLU, 0);
board.play(Disk::BLU, 1);
board.play(Disk::RED, 2);
board.play(Disk::BLU, 3);
assert_eq!(2, scan(&board.columns, &(0, 0), Direction::RIGHT, 4));
assert_eq!(1, scan(&board.columns, &(0, 3), Direction::LEFT, 4));
board.play(Disk::P2, 0);
board.play(Disk::P2, 1);
board.play(Disk::P1, 2);
board.play(Disk::P2, 3);
assert_eq!(2, scan(&board.columns, &(0, 0), Direction::Right, 4));
assert_eq!(1, scan(&board.columns, &(0, 3), Direction::Left, 4));
}
#[test]
fn scan_diag1() {
let mut board = Board::default();
board.play(Disk::BLU, 0);
board.play(Disk::RED, 1);
board.play(Disk::BLU, 1);
board.play(Disk::RED, 2);
board.play(Disk::RED, 2);
board.play(Disk::BLU, 2);
board.play(Disk::RED, 3);
board.play(Disk::RED, 3);
board.play(Disk::RED, 3);
board.play(Disk::BLU, 3);
assert_eq!(4, scan(&board.columns, &(0, 0), Direction::UPRIGHT, 4));
assert_eq!(4, scan(&board.columns, &(3, 3), Direction::DOWNLEFT, 4));
board.play(Disk::P2, 0);
board.play(Disk::P1, 1);
board.play(Disk::P2, 1);
board.play(Disk::P1, 2);
board.play(Disk::P1, 2);
board.play(Disk::P2, 2);
board.play(Disk::P1, 3);
board.play(Disk::P1, 3);
board.play(Disk::P1, 3);
board.play(Disk::P2, 3);
assert_eq!(4, scan(&board.columns, &(0, 0), Direction::UpRight, 4));
assert_eq!(4, scan(&board.columns, &(3, 3), Direction::DownLeft, 4));
}
#[test]
fn scan_diag2() {
let mut board = Board::default();
board.play(Disk::BLU, 3);
board.play(Disk::RED, 2);
board.play(Disk::BLU, 2);
board.play(Disk::RED, 1);
board.play(Disk::RED, 1);
board.play(Disk::BLU, 1);
board.play(Disk::RED, 0);
board.play(Disk::RED, 0);
board.play(Disk::RED, 0);
board.play(Disk::BLU, 0);
board.play(Disk::P2, 3);
board.play(Disk::P1, 2);
board.play(Disk::P2, 2);
board.play(Disk::P1, 1);
board.play(Disk::P1, 1);
board.play(Disk::P2, 1);
board.play(Disk::P1, 0);
board.play(Disk::P1, 0);
board.play(Disk::P1, 0);
board.play(Disk::P2, 0);
dbg!(&board.columns.as_columns());
assert_eq!(4, scan(&board.columns, &(0, 3), Direction::UPLEFT, 4));
assert_eq!(4, scan(&board.columns, &(3, 0), Direction::DOWNRIGHT, 4));
assert_eq!(4, scan(&board.columns, &(0, 3), Direction::UpLeft, 4));
assert_eq!(4, scan(&board.columns, &(3, 0), Direction::DownRight, 4));
}
#[test]
fn variant_eq_test() {
assert!(score_checkers::variant_eq(&Disk::RED, &Disk::RED));
assert!(matches!(Disk::RED, Disk::RED));
assert!(matches!(&Disk::BLU, &Disk::BLU));
assert!(!score_checkers::variant_eq(&Disk::BLU, &Disk::RED));
assert!(!matches!(Disk::BLU, Disk::RED));
assert!(score_checkers::variant_eq(&Disk::P1, &Disk::P1));
assert!(matches!(Disk::P1, Disk::P1));
assert!(matches!(&Disk::P2, &Disk::P2));
assert!(!score_checkers::variant_eq(&Disk::P2, &Disk::P1));
assert!(!matches!(Disk::P2, Disk::P1));
}
#[test]
fn game_over_test() {
let mut board = Board::default();
assert!(!board.game_over());
board.columns = Array2D::filled_with(Disk::BLU, 7, 6);
board.columns = Array2D::filled_with(Disk::P2, 7, 6);
assert!(board.game_over());
board.columns.set(0, 0, Disk::EMPTY).expect("balls");
assert!(!board.game_over());

View File

@@ -1,19 +1,110 @@
use std::time::Instant;
use oxidised4::{
bored::{GameState, MenuState, PlayState, Strategy},
gamedata::{
algorithms::{minimax_decision, minimax_decision_pruning},
Disk,
},
};
use raylib::prelude::*;
const NROW: i32 = 6;
const NCOL: i32 = 7;
const BOARDSTART: (i32, i32) = (0, 0);
fn main() {
let (mut rl, thread) = raylib::init().size(640, 480).title("Hello, World").build();
let _rust_orange = Color::new(222, 165, 132, 255);
let _ray_white = Color::new(255, 255, 255, 255);
//images
let board_image = Image::load_image("resouces/board.png").expect("WHAT DA HAILLL");
let _ = rl
.load_texture(&thread, "resouces/board.png")
.expect("couldn't load texture :(");
let _ = rl
.load_texture(&thread, "resouces/bracc.png")
.expect("couldn't load texture :(");
let circle_image = Image::load_image("resouces/bracc.png").expect("WHAT DA HAILLL");
let _ = rl
.load_texture(&thread, "resouces/bracc.png")
.expect("couldn't load texture :(");
//textures
let board_texture = rl
.load_texture_from_image(&thread, &board_image)
.expect("WHAT DA HAILL");
let circle_texture = rl
.load_texture_from_image(&thread, &circle_image)
.expect("WHAT DA HAILL");
let square_widf = board_texture.width / NCOL;
dbg!(square_widf);
let square_heif = board_texture.height / NROW;
let _square_wewant = (square_widf * NROW / 2, square_heif * 3 / 2);
let _square_center = square_widf / 2;
//7,9 are the values to center the circle
let mut state: GameState = GameState::MainMenu(MenuState::default());
let mut strategy = Strategy::MiniMax;
let mut difficulty = 3;
rl.set_target_fps(60);
while !rl.window_should_close() {
let pressed_key = rl.get_key_pressed();
let mut d = rl.begin_drawing(&thread);
d.clear_background(Color::WHITE);
if let Some(pressed_key) = pressed_key {
// Certain keyboards may have keys raylib does not expect. Uncomment this line if so.
// let pressed_key: u32 = unsafe { std::mem::transmute(pressed_key) };
d.draw_text(&format!("{:?}", pressed_key), 100, 12, 10, Color::BLACK);
match &mut state {
GameState::MainMenu(ref mut mstate) => {
if mstate.init(&d) {
strategy = mstate.strategy.clone();
difficulty = mstate.difficulty.clone();
state = GameState::Play(PlayState::default());
}
}
GameState::Play(ref mut state) => {
match state.player_turn {
true => {
if d.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON) {
if let Some(column) = get_mouse_column(&d, square_widf) {
state.play_human(column);
}
}
}
false => {
dbg!();
let time = Instant::now();
match strategy {
Strategy::MiniMax => state.play_cpu(&difficulty, minimax_decision),
Strategy::AlphaBeta => {
state.play_cpu(&difficulty, minimax_decision_pruning)
}
};
println!(
"Time elapsed in cpu turn: {} ms",
time.elapsed().as_millis()
);
}
}
for circle in state.clone().circles {
let (x, y, disk) = circle;
let color = match disk {
Disk::P1 => Color::RED,
Disk::P2 => Color::YELLOW,
Disk::EMPTY => Color::WHITE,
};
d.draw_texture(&circle_texture, x, y, color);
}
d.clear_background(Color::WHITE);
d.draw_texture(&board_texture, BOARDSTART.0, BOARDSTART.1, Color::VIOLET);
}
}
}
}
const STARTY: i32 = 9;
fn get_mouse_column(rl: &RaylibHandle, sw: i32) -> Option<i32> {
//row,col return
let mouse_pos = rl.get_mouse_x();
for num in 1..NCOL + 1 {
if (mouse_pos > sw * (num - 1) + STARTY) && (mouse_pos < sw * (num) - STARTY) {
return Some(num - 1);
}
}
None
}