Skip to content

Commit

Permalink
scores are numbers now
Browse files Browse the repository at this point in the history
  • Loading branch information
wvdschel committed Nov 30, 2022
1 parent 9692dd1 commit f80e894
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 149 deletions.
52 changes: 9 additions & 43 deletions src/logic/scoring/tournament.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,15 @@ use crate::logic::{voronoi, Game};

use super::{kills, turns_survived};

#[derive(Copy, PartialOrd, Ord, Clone, PartialEq, Eq, Default)]
pub struct TournamentScore {
alive: bool,
kills: usize,
tiles: usize,
turns: usize,
}

impl std::fmt::Display for TournamentScore {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"alive={}, kills={}, tiles={}, turns={}",
self.alive, self.kills, self.tiles, self.turns
))
pub fn tournament(game: &Game) -> i64 {
let mut score = 0;
if game.you.dead() {
score -= 1_000_000_000;
} else {
score += voronoi::me(game) as i64 * 1_000;
}
}

pub fn tournament(game: &Game) -> TournamentScore {
let dead = game.you.dead();
TournamentScore {
alive: !dead,
tiles: if !dead { voronoi::me(game) } else { 0 },
kills: kills(game),
turns: turns_survived(game),
}
}

#[test]
fn test_weird_choice() {
// best move is Right with score alive=true, kills=0, tiles=1, turns=109
// best move is Left with score alive=false, kills=1, tiles=0, turns=108
let s1 = TournamentScore {
alive: true,
kills: 0,
tiles: 1,
turns: 109,
};
let s2 = TournamentScore {
alive: false,
kills: 1,
tiles: 0,
turns: 108,
};
score += turns_survived(game) as i64;
score += kills(game) as i64 * 10_000_000;

assert_eq!(s1 > s2, true);
score
}
82 changes: 28 additions & 54 deletions src/snakes/spaceheater3/alphabeta.rs
Original file line number Diff line number Diff line change
@@ -1,81 +1,55 @@
use std::sync::RwLock;
use std::sync::atomic::{AtomicI64, Ordering};

pub struct AlphaBeta<'a, S: Ord + Clone> {
parent: Option<&'a AlphaBeta<'a, S>>,
alpha: RwLock<Option<S>>,
beta: RwLock<Option<S>>,
pub struct AlphaBeta<'a> {
parent: Option<&'a AlphaBeta<'a>>,
alpha: AtomicI64,
beta: AtomicI64,
}

impl<'a, S: Ord + Clone> AlphaBeta<'a, S> {
pub fn new(a: Option<S>, b: Option<S>) -> Self {
impl<'a> AlphaBeta<'a> {
pub fn new(a: i64, b: i64) -> Self {
Self {
parent: None,
alpha: RwLock::new(a),
beta: RwLock::new(b),
alpha: AtomicI64::new(a),
beta: AtomicI64::new(b),
}
}

pub fn new_child(&'a self) -> Self {
Self {
parent: Some(self),
alpha: RwLock::new(self.alpha.read().unwrap().clone()),
beta: RwLock::new(self.beta.read().unwrap().clone()),
alpha: AtomicI64::new(self.alpha.load(Ordering::Relaxed)),
beta: AtomicI64::new(self.beta.load(Ordering::Relaxed)),
}
}

pub fn new_alpha_score(&self, a: S) {
let next_score = Some(a);
let new_alpha = *self.alpha.read().unwrap() < next_score;

if new_alpha {
let mut alpha_write = self.alpha.write().unwrap();
if *alpha_write < next_score {
*alpha_write = next_score;
}
}
#[inline(always)]
pub fn new_alpha_score(&self, a: i64) -> i64 {
self.alpha.fetch_max(a, Ordering::Relaxed)
}

pub fn new_beta_score(&self, b: S) {
let new_beta = self
.beta
.read()
.unwrap()
.as_ref()
.map_or(true, |old_b| *old_b > b);

if new_beta {
let mut beta_write = self.beta.write().unwrap();
let next_score = Some(b);
if *beta_write == None || *beta_write > next_score {
*beta_write = next_score;
}
}
#[inline(always)]
pub fn new_beta_score(&self, b: i64) -> i64 {
self.beta.fetch_min(b, Ordering::Relaxed)
}

pub fn should_be_pruned(&self) -> bool {
let mut max_alpha = self.alpha.read().unwrap().clone();
let mut max_alpha = self.alpha.load(Ordering::Relaxed);
let mut min_beta = self.beta.load(Ordering::Relaxed);
let mut next = self;
while let Some(v) = next.parent {
let other_alpha = v.alpha.read().unwrap();
if *other_alpha > max_alpha {
max_alpha = other_alpha.clone();
let next_alpha = v.alpha.load(Ordering::Relaxed);
let next_beta = v.beta.load(Ordering::Relaxed);
if next_alpha > max_alpha {
max_alpha = next_alpha
}
next = v;
}

let mut next = self;
loop {
let beta = next.beta.read().unwrap();
if *beta != None {
if max_alpha > *beta {
return true;
}
if next_beta < min_beta {
min_beta = next_beta
}
if let Some(p) = next.parent {
next = p;
} else {
break;
if max_alpha >= min_beta {
return true;
}
next = v;
}

false
Expand Down
28 changes: 13 additions & 15 deletions src/snakes/spaceheater3/max.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use rayon::prelude::*;

use std::{
cmp,
fmt::Display,
sync::{
atomic::{AtomicUsize, Ordering},
Arc, RwLock,
Expand All @@ -17,13 +16,13 @@ use crate::{

use super::{alphabeta::AlphaBeta, min::MinimizingNode, util::certain_death};

pub struct MaximizingNode<S: Ord + Display + Clone + 'static> {
pub struct MaximizingNode {
pub(super) game: Game,
pub(super) score: Option<(Direction, S)>,
pub(super) children: Vec<MinimizingNode<S>>,
pub(super) score: Option<(Direction, i64)>,
pub(super) children: Vec<MinimizingNode>,
}

impl<'a, S: Ord + Display + Clone + 'static> MaximizingNode<S> {
impl MaximizingNode {
pub fn new(game: Game) -> Self {
Self {
game,
Expand All @@ -48,7 +47,7 @@ impl<'a, S: Ord + Display + Clone + 'static> MaximizingNode<S> {

fn check_bounds<FScore>(&mut self, max_depth: usize, score_fn: &FScore) -> bool
where
FScore: Fn(&Game) -> S,
FScore: Fn(&Game) -> i64,
{
if self.game.you.dead() {
if self.score == None {
Expand Down Expand Up @@ -94,7 +93,7 @@ impl<'a, S: Ord + Display + Clone + 'static> MaximizingNode<S> {
};
strings.push(format!("{}", self.game));

let mut children: std::vec::Vec<&MinimizingNode<S>> = self.children.iter().collect();
let mut children: std::vec::Vec<&MinimizingNode> = self.children.iter().collect();
children.sort_by(|c1, c2| c1.cmp_scores(c2));
for c in children {
strings.push(c.format_tree(depth));
Expand All @@ -113,17 +112,17 @@ impl<'a, S: Ord + Display + Clone + 'static> MaximizingNode<S> {
}
}

impl<'a, S: Ord + Display + Clone + Send + Sync + 'static> MaximizingNode<S> {
impl MaximizingNode {
pub fn solve<FScore>(
&mut self,
deadline: &Instant,
max_depth: usize,
score_fn: &FScore,
alpha_beta: &AlphaBeta<'_, S>,
alpha_beta: &AlphaBeta<'_>,
threads: f32,
) -> (Option<(Direction, S)>, usize)
) -> (Option<(Direction, i64)>, usize)
where
FScore: Fn(&Game) -> S + Sync,
FScore: Fn(&Game) -> i64 + Sync,
{
if Instant::now() > *deadline {
return (None, 0);
Expand Down Expand Up @@ -152,7 +151,7 @@ impl<'a, S: Ord + Display + Clone + Send + Sync + 'static> MaximizingNode<S> {
let alpha_beta = alpha_beta.new_child();
let total_node_count = AtomicUsize::new(0);

let solver = |min_node: &mut MinimizingNode<S>| {
let solver = |min_node: &mut MinimizingNode| {
if alpha_beta.should_be_pruned() {
return;
}
Expand Down Expand Up @@ -180,9 +179,8 @@ impl<'a, S: Ord + Display + Clone + Send + Sync + 'static> MaximizingNode<S> {
if top_score_write.1 < next_score {
*top_score_write = (min_node.my_move, next_score.clone());
}
alpha_beta.new_alpha_score(next_score.unwrap());
}

alpha_beta.new_alpha_score(next_score.unwrap());
};
if parallel {
let _res: Vec<()> = self.children.par_iter_mut().map(solver).collect();
Expand All @@ -203,7 +201,7 @@ impl<'a, S: Ord + Display + Clone + Send + Sync + 'static> MaximizingNode<S> {
}
}

impl<'a, S: Ord + Display + Clone + 'static> std::fmt::Display for MaximizingNode<S> {
impl std::fmt::Display for MaximizingNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.format_tree(0).as_str())
}
Expand Down
46 changes: 21 additions & 25 deletions src/snakes/spaceheater3/min.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ use rayon::prelude::*;

use std::{
cmp,
fmt::Display,
sync::{
atomic::{AtomicUsize, Ordering},
Arc, RwLock,
atomic::{AtomicI64, AtomicUsize, Ordering},
Arc,
},
time::Instant,
};
Expand All @@ -17,13 +16,13 @@ use crate::{

use super::{alphabeta::AlphaBeta, max::MaximizingNode, util::all_sensible_enemy_moves};

pub struct MinimizingNode<S: Ord + Display + Clone + 'static> {
pub struct MinimizingNode {
pub my_move: Direction,
pub(super) score: Option<S>,
pub(super) children: Vec<MaximizingNode<S>>,
pub(super) score: Option<i64>,
pub(super) children: Vec<MaximizingNode>,
}

impl<'a, S: Ord + Display + Clone + 'static> MinimizingNode<S> {
impl MinimizingNode {
pub fn new(my_move: Direction) -> Self {
Self {
my_move,
Expand Down Expand Up @@ -74,7 +73,7 @@ impl<'a, S: Ord + Display + Clone + 'static> MinimizingNode<S> {
None => {}
};

let mut children: std::vec::Vec<&MaximizingNode<S>> = self.children.iter().collect();
let mut children: std::vec::Vec<&MaximizingNode> = self.children.iter().collect();
children.sort_by(|c1, c2| c1.cmp_scores(c2));
for c in children {
strings.push(c.format_tree(depth + 1));
Expand All @@ -92,18 +91,18 @@ impl<'a, S: Ord + Display + Clone + 'static> MinimizingNode<S> {
}
}

impl<'a, S: Ord + Display + Clone + Send + Sync + 'static> MinimizingNode<S> {
impl MinimizingNode {
pub fn solve<FScore>(
&mut self,
game: Arc<&Game>,
deadline: &Instant,
max_depth: usize,
score_fn: &FScore,
alpha_beta: &AlphaBeta<'_, S>,
alpha_beta: &AlphaBeta<'_>,
threads: f32,
) -> (Option<S>, usize)
) -> (Option<i64>, usize)
where
FScore: Fn(&Game) -> S + Sync,
FScore: Fn(&Game) -> i64 + Sync,
{
let game = *game.as_ref();

Expand All @@ -114,11 +113,11 @@ impl<'a, S: Ord + Display + Clone + Send + Sync + 'static> MinimizingNode<S> {
(false, threads)
};

let min_score: RwLock<Option<S>> = RwLock::new(None);
let min_score: AtomicI64 = AtomicI64::new(i64::MAX);
let alpha_beta = alpha_beta.new_child();
let total_node_count = AtomicUsize::new(0);

let solver = |max_node: &mut MaximizingNode<S>| {
let solver = |max_node: &mut MaximizingNode| {
if alpha_beta.should_be_pruned() {
return;
}
Expand All @@ -133,16 +132,9 @@ impl<'a, S: Ord + Display + Clone + Send + Sync + 'static> MinimizingNode<S> {
};

total_node_count.fetch_add(node_count, Ordering::Relaxed);

let mut min_score_write = min_score.write().unwrap();
if *min_score_write != None {
*min_score_write =
Some(cmp::min(min_score_write.as_ref().unwrap(), &next_score).clone());
} else {
*min_score_write = Some(next_score.clone());
if min_score.fetch_min(next_score, Ordering::Relaxed) > next_score {
alpha_beta.new_beta_score(next_score);
}

alpha_beta.new_beta_score(next_score);
};

if parallel {
Expand All @@ -156,8 +148,12 @@ impl<'a, S: Ord + Display + Clone + Send + Sync + 'static> MinimizingNode<S> {
return (None, total_node_count.load(Ordering::Relaxed));
}

let min_score = min_score.read().unwrap().clone();
self.score = min_score.clone();
let min_score = min_score.load(Ordering::Relaxed);
let min_score = if min_score == i64::MAX {
None
} else {
Some(min_score)
};
(min_score, total_node_count.load(Ordering::Relaxed))
}
}
Loading

0 comments on commit f80e894

Please sign in to comment.