Skip to content
This repository has been archived by the owner on Jan 22, 2024. It is now read-only.

Commit

Permalink
forced analysis lines
Browse files Browse the repository at this point in the history
  • Loading branch information
MinusKelvin committed Mar 19, 2020
1 parent f2b69c7 commit f3be020
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 12 deletions.
45 changes: 40 additions & 5 deletions bot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@ impl Interface {
self.dead = true;
}
}

/// Specifies a line that Cold Clear should analyze before making any moves. The path is
/// sensitive to T-spin status.
pub fn force_analysis_line(&mut self, path: Vec<FallingPiece>) {
if self.send.send(BotMsg::ForceAnalysisLine(path)).is_err() {
self.dead = true;
}
}
}

enum BotMsg {
Expand All @@ -148,13 +156,15 @@ enum BotMsg {
combo: u32
},
NewPiece(Piece),
NextMove(u32)
NextMove(u32),
ForceAnalysisLine(Vec<FallingPiece>)
}

pub struct BotState<E: Evaluator> {
tree: TreeState<E::Value, E::Reward>,
options: Options,
eval: Arc<E>,
forced_analysis_lines: Vec<Vec<FallingPiece>>
}

pub struct Thinker<E: Evaluator> {
Expand All @@ -175,7 +185,8 @@ impl<E: Evaluator> BotState<E> {
BotState {
tree: TreeState::create(board, options.use_hold),
options,
eval: Arc::new(eval)
eval: Arc::new(eval),
forced_analysis_lines: vec![],
}
}

Expand All @@ -184,7 +195,9 @@ impl<E: Evaluator> BotState<E> {
/// Returns `Err(true)` if a thinking cycle can be preformed, but it couldn't find
pub fn think(&mut self) -> Result<Thinker<E>, bool> {
if self.tree.nodes < self.options.max_nodes && !self.tree.is_dead() {
if let Some((node, board)) = self.tree.find_and_mark_leaf() {
if let Some((node, board)) = self.tree.find_and_mark_leaf(
&mut self.forced_analysis_lines
) {
return Ok(Thinker {
node, board,
options: self.options,
Expand Down Expand Up @@ -216,11 +229,27 @@ impl<E: Evaluator> BotState<E> {
}

pub fn reset(&mut self, field: [[bool; 10]; 40], b2b: bool, combo: u32) {
self.tree.reset(field, b2b, combo);
let plan = self.tree.get_plan();
if let Some(garbage_lines) = self.tree.reset(field, b2b, combo) {
for path in &mut self.forced_analysis_lines {
for mv in path {
mv.y += garbage_lines;
}
}
let mut prev_best_path = vec![];
for mv in plan {
let mut mv = mv.0;
mv.y += garbage_lines;
prev_best_path.push(mv);
}
self.forced_analysis_lines.push(prev_best_path);
} else {
self.forced_analysis_lines.clear();
}
}

pub fn min_thinking_reached(&self) -> bool {
self.tree.nodes > self.options.min_nodes
self.tree.nodes > self.options.min_nodes && self.forced_analysis_lines.is_empty()
}

pub fn next_move(&mut self, incoming: u32, f: impl FnOnce(Move, Info)) -> bool {
Expand Down Expand Up @@ -260,6 +289,10 @@ impl<E: Evaluator> BotState<E> {

true
}

pub fn force_analysis_line(&mut self, path: Vec<FallingPiece>) {
self.forced_analysis_lines.push(path);
}
}

impl<E: Evaluator> Thinker<E> {
Expand Down Expand Up @@ -385,6 +418,7 @@ fn run(
board.b2b_bonus = b2b;
}
Ok(BotMsg::NextMove(incoming)) => do_move = Some(incoming),
Ok(BotMsg::ForceAnalysisLine(path)) => {}
}
}

Expand All @@ -404,6 +438,7 @@ fn run(
Ok(BotMsg::NewPiece(piece)) => bot.add_next_piece(piece),
Ok(BotMsg::Reset { field, b2b, combo }) => bot.reset(field, b2b, combo),
Ok(BotMsg::NextMove(incoming)) => do_move = Some(incoming),
Ok(BotMsg::ForceAnalysisLine(path)) => bot.force_analysis_line(path)
}

if let Some(incoming) = do_move {
Expand Down
70 changes: 63 additions & 7 deletions bot/src/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,31 @@ impl<E: Evaluation<R>, R: Clone> TreeState<E, R> {
this
}

pub fn reset(&mut self, field: [[bool; 10]; 40], b2b: bool, combo: u32) {
pub fn reset(&mut self, field: [[bool; 10]; 40], b2b: bool, combo: u32) -> Option<i32> {
let garbage_lines;
if b2b == self.board.b2b_bonus && combo == self.board.combo {
let mut b = Board::<u16>::new();
b.set_field(field);
let dif = self.board.column_heights().iter()
.zip(b.column_heights().iter())
.map(|(&y1, &y2)| y2 - y1)
.min().unwrap();
let mut is_garbage_receive = true;
for y in 0..(40 - dif) {
if b.get_row(y + dif) != self.board.get_row(y) {
is_garbage_receive = false;
break;
}
}
if is_garbage_receive {
garbage_lines = Some(dif);
} else {
garbage_lines = None;
}
} else {
garbage_lines = None;
}

self.board.set_field(field);
self.board.combo = combo;
self.board.b2b_bonus = b2b;
Expand All @@ -146,17 +170,35 @@ impl<E: Evaluation<R>, R: Clone> TreeState<E, R> {
marked: false,
death: false
});

garbage_lines
}

/// To be called by a worker looking to expand the tree. `update_known`, `update_speculated`, or
/// `unmark` should be called to provide the generated children. If this returns `None`, the
/// leaf found is already being expanded by another worker, and you should try again later.
pub fn find_and_mark_leaf(&mut self) -> Option<(NodeId, Board)> {
pub fn find_and_mark_leaf(
&mut self, forced_analysis_lines: &mut Vec<Vec<FallingPiece>>
) -> Option<(NodeId, Board)> {
if self.is_dead() {
return None
}
let mut current = 0;
loop {

for i in 0..forced_analysis_lines.len() {
if let Some((node, board, done)) = self.descend(&forced_analysis_lines[i]) {
if done {
forced_analysis_lines.remove(i);
}
return Some((node, board))
}
}

self.descend(&[]).map(|(n,b,_)| (n,b))
}

fn descend(&mut self, mut path: &[FallingPiece]) -> Option<(NodeId, Board, bool)> {
let mut current = self.root;
'descend: loop {
match self.children[current as usize] {
None => {
if self.trees[current as usize].marked {
Expand All @@ -165,12 +207,25 @@ impl<E: Evaluation<R>, R: Clone> TreeState<E, R> {
self.trees[current as usize].marked = true;
return Some((
NodeId(self.generation, current),
self.pieces.rebuild_board(&self.trees[current as usize].board)
self.pieces.rebuild_board(&self.trees[current as usize].board),
path.is_empty()
));
}
},
Some(Children::Known(start, len)) =>
current = pick(&self.trees, &self.childs[start as usize..(start+len) as usize]),
Some(Children::Known(start, len)) => {
let range = start as usize..(start + len) as usize;
if let [next, rest @ ..] = path {
for c in &self.childs[range.clone()] {
if c.mv == *next {
current = c.node;
path = rest;
continue 'descend;
}
}
}
current = pick(&self.trees, &self.childs[range]);
path = &[];
}
Some(Children::Speculation(c)) => {
let mut pick_from = ArrayVec::<[_; 7]>::new();
for (_, c) in c {
Expand All @@ -182,6 +237,7 @@ impl<E: Evaluation<R>, R: Clone> TreeState<E, R> {
}
let &(start, len) = pick_from.choose(&mut thread_rng()).unwrap();
current = pick(&self.trees, &self.childs[start as usize..(start+len) as usize]);
path = &[];
}
}
}
Expand Down

0 comments on commit f3be020

Please sign in to comment.