Fix rand not compilable for wasm, impl game logic
"rand" crate was not compilable for wasm-unknown-unknown target, so an alternative "oorandom" crate was substituted in. Basic game win/draw detection logic added.
This commit is contained in:
parent
2389441207
commit
9e9bb0758c
6 changed files with 158 additions and 64 deletions
61
front_end/Cargo.lock
generated
61
front_end/Cargo.lock
generated
|
@ -41,23 +41,12 @@ name = "four_line_dropper_frontend"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"rand",
|
"oorandom",
|
||||||
"wasm-logger",
|
"wasm-logger",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"yew",
|
"yew",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "getrandom"
|
|
||||||
version = "0.2.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"wasi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gloo"
|
name = "gloo"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
|
@ -201,12 +190,6 @@ version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libc"
|
|
||||||
version = "0.2.119"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.14"
|
version = "0.4.14"
|
||||||
|
@ -217,10 +200,10 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "oorandom"
|
||||||
version = "0.2.16"
|
version = "11.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-error"
|
name = "proc-macro-error"
|
||||||
|
@ -264,36 +247,6 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand"
|
|
||||||
version = "0.8.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"rand_chacha",
|
|
||||||
"rand_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_chacha"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
|
||||||
dependencies = [
|
|
||||||
"ppv-lite86",
|
|
||||||
"rand_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.6.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.9"
|
version = "1.0.9"
|
||||||
|
@ -386,12 +339,6 @@ version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasi"
|
|
||||||
version = "0.10.2+wasi-snapshot-preview1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.79"
|
version = "0.2.79"
|
||||||
|
|
|
@ -10,4 +10,4 @@ yew = "0.19"
|
||||||
log = "0.4.6"
|
log = "0.4.6"
|
||||||
wasm-logger = "0.2.0"
|
wasm-logger = "0.2.0"
|
||||||
web-sys = { version = "0.3.56", features = ["Window", "Document", "Element"] }
|
web-sys = { version = "0.3.56", features = ["Window", "Document", "Element"] }
|
||||||
rand = "0.8.5"
|
oorandom = "11.1.3"
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use crate::constants::{AI_EASY_MAX_CHOICES, AI_NORMAL_MAX_CHOICES, COLS, ROWS};
|
use crate::constants::{AI_EASY_MAX_CHOICES, AI_NORMAL_MAX_CHOICES, COLS, ROWS};
|
||||||
|
use crate::random_helper::get_seeded_random;
|
||||||
use crate::state::{BoardState, BoardType, Turn};
|
use crate::state::{BoardState, BoardType, Turn};
|
||||||
|
|
||||||
use rand::{thread_rng, Rng};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum AIDifficulty {
|
pub enum AIDifficulty {
|
||||||
Easy,
|
Easy,
|
||||||
|
@ -63,6 +62,8 @@ pub fn get_ai_choice(
|
||||||
player: Turn,
|
player: Turn,
|
||||||
board: &BoardType,
|
board: &BoardType,
|
||||||
) -> Result<SlotChoice, String> {
|
) -> Result<SlotChoice, String> {
|
||||||
|
let mut rng = get_seeded_random()?;
|
||||||
|
|
||||||
let mut utilities = Vec::with_capacity(COLS as usize);
|
let mut utilities = Vec::with_capacity(COLS as usize);
|
||||||
for i in 0..(COLS as usize) {
|
for i in 0..(COLS as usize) {
|
||||||
let slot = i.into();
|
let slot = i.into();
|
||||||
|
@ -82,11 +83,11 @@ pub fn get_ai_choice(
|
||||||
let mut utilities: Vec<(usize, f64)> = utilities.into_iter().enumerate().collect();
|
let mut utilities: Vec<(usize, f64)> = utilities.into_iter().enumerate().collect();
|
||||||
if utilities.len() > 1 {
|
if utilities.len() > 1 {
|
||||||
for i in 1..utilities.len() {
|
for i in 1..utilities.len() {
|
||||||
utilities.swap(i, thread_rng().gen_range(0..=i));
|
utilities.swap(i, rng.rand_range(0..((i + 1) as u32)) as usize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let pick_some_of_choices = |amount: usize| -> Result<SlotChoice, String> {
|
let mut pick_some_of_choices = |amount: usize| -> Result<SlotChoice, String> {
|
||||||
let mut maximums: BTreeMap<i64, usize> = BTreeMap::new();
|
let mut maximums: BTreeMap<i64, usize> = BTreeMap::new();
|
||||||
for (idx, utility) in &utilities {
|
for (idx, utility) in &utilities {
|
||||||
// f64 cannot be used as Key since it doesn't implement Ord.
|
// f64 cannot be used as Key since it doesn't implement Ord.
|
||||||
|
@ -94,7 +95,7 @@ pub fn get_ai_choice(
|
||||||
// order.
|
// order.
|
||||||
let mut utility_value = (utility * 10000.0) as i64;
|
let mut utility_value = (utility * 10000.0) as i64;
|
||||||
while maximums.contains_key(&utility_value) {
|
while maximums.contains_key(&utility_value) {
|
||||||
utility_value += thread_rng().gen_range(-3..=3);
|
utility_value += rng.rand_range(0..7) as i64 - 3;
|
||||||
}
|
}
|
||||||
maximums.insert(utility_value, *idx);
|
maximums.insert(utility_value, *idx);
|
||||||
}
|
}
|
||||||
|
@ -108,7 +109,7 @@ pub fn get_ai_choice(
|
||||||
|
|
||||||
// don't use random if only 1 item is to be picked
|
// don't use random if only 1 item is to be picked
|
||||||
let random_number: usize = if mod_amount > 1 {
|
let random_number: usize = if mod_amount > 1 {
|
||||||
thread_rng().gen::<usize>() % mod_amount
|
rng.rand_u32() as usize % mod_amount
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
132
front_end/src/game_logic.rs
Normal file
132
front_end/src/game_logic.rs
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
use crate::constants::{COLS, ROWS};
|
||||||
|
use crate::state::{BoardState, BoardType};
|
||||||
|
|
||||||
|
/// Returns a BoardState if win/draw, None if game is still going
|
||||||
|
pub fn check_win_draw(board: &BoardType) -> Option<BoardState> {
|
||||||
|
let mut has_empty_slot = false;
|
||||||
|
for slot in board {
|
||||||
|
match slot.get() {
|
||||||
|
BoardState::Empty => {
|
||||||
|
has_empty_slot = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
BoardState::Cyan | BoardState::Magenta => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_empty_slot {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let check_result = |state| -> Option<BoardState> {
|
||||||
|
match state {
|
||||||
|
BoardState::Empty => None,
|
||||||
|
BoardState::Cyan => Some(BoardState::Cyan),
|
||||||
|
BoardState::Magenta => Some(BoardState::Magenta),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// check horizontals
|
||||||
|
for y in 0..(ROWS as usize) {
|
||||||
|
for x in 0..((COLS - 3) as usize) {
|
||||||
|
let result = check_result(has_right_horizontal_at_idx(x + y * (COLS as usize), board));
|
||||||
|
if result.is_some() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check verticals
|
||||||
|
for y in 0..((ROWS - 3) as usize) {
|
||||||
|
for x in 0..(COLS as usize) {
|
||||||
|
let result = check_result(has_down_vertical_at_idx(x + y * (COLS as usize), board));
|
||||||
|
if result.is_some() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check up diagonals
|
||||||
|
for y in 3..(ROWS as usize) {
|
||||||
|
for x in 0..((COLS - 3) as usize) {
|
||||||
|
let result = check_result(has_right_up_diagonal_at_idx(x + y * (COLS as usize), board));
|
||||||
|
if result.is_some() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check down diagonals
|
||||||
|
for y in 0..((ROWS - 3) as usize) {
|
||||||
|
for x in 0..((COLS - 3) as usize) {
|
||||||
|
let result = check_result(has_right_down_diagonal_at_idx(
|
||||||
|
x + y * (COLS as usize),
|
||||||
|
board,
|
||||||
|
));
|
||||||
|
if result.is_some() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_right_horizontal_at_idx(idx: usize, board: &BoardType) -> BoardState {
|
||||||
|
let state_at_idx = board[idx].get();
|
||||||
|
if idx % (COLS as usize) < (COLS as usize) - 3 {
|
||||||
|
for x in 0..=3 {
|
||||||
|
if board[idx + x].get() != state_at_idx {
|
||||||
|
break;
|
||||||
|
} else if x == 3 {
|
||||||
|
return state_at_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BoardState::Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_down_vertical_at_idx(idx: usize, board: &BoardType) -> BoardState {
|
||||||
|
let state_at_idx = board[idx].get();
|
||||||
|
if idx / (COLS as usize) < (ROWS as usize) - 3 {
|
||||||
|
for y in 0..=3 {
|
||||||
|
if board[idx + y * (COLS as usize)].get() != state_at_idx {
|
||||||
|
break;
|
||||||
|
} else if y == 3 {
|
||||||
|
return state_at_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BoardState::Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_right_up_diagonal_at_idx(idx: usize, board: &BoardType) -> BoardState {
|
||||||
|
let state_at_idx = board[idx].get();
|
||||||
|
if idx % (COLS as usize) < (COLS as usize) - 3 && idx / (COLS as usize) > 2 {
|
||||||
|
for i in 0..=3 {
|
||||||
|
if board[idx + i - i * (COLS as usize)].get() != state_at_idx {
|
||||||
|
break;
|
||||||
|
} else if i == 3 {
|
||||||
|
return state_at_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BoardState::Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_right_down_diagonal_at_idx(idx: usize, board: &BoardType) -> BoardState {
|
||||||
|
let state_at_idx = board[idx].get();
|
||||||
|
if idx % (COLS as usize) < (COLS as usize) - 3 && idx / (COLS as usize) < (ROWS as usize) - 3 {
|
||||||
|
for i in 0..=3 {
|
||||||
|
if board[idx + i + i * (COLS as usize)].get() != state_at_idx {
|
||||||
|
break;
|
||||||
|
} else if i == 3 {
|
||||||
|
return state_at_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BoardState::Empty
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
mod ai;
|
mod ai;
|
||||||
mod constants;
|
mod constants;
|
||||||
|
mod game_logic;
|
||||||
|
mod random_helper;
|
||||||
mod state;
|
mod state;
|
||||||
mod yew_components;
|
mod yew_components;
|
||||||
|
|
||||||
|
|
12
front_end/src/random_helper.rs
Normal file
12
front_end/src/random_helper.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
use oorandom::Rand32;
|
||||||
|
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
pub fn get_seeded_random() -> Result<Rand32, String> {
|
||||||
|
let now = SystemTime::now();
|
||||||
|
let duration = now
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.map_err(|e| format!("{}", e))?;
|
||||||
|
|
||||||
|
Ok(Rand32::new(duration.as_secs()))
|
||||||
|
}
|
Loading…
Reference in a new issue