Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refine high level routines #50

Merged
merged 14 commits into from
May 7, 2024
6 changes: 3 additions & 3 deletions src/book.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl Book {
let reader = BufReader::new(f);
let mut book = Book::new();
for line in reader.lines() {
book.append(Record::parse(&line?)?)?;
book.append(line?.parse::<Record>()?)?;
}
Ok(book)
}
Expand Down Expand Up @@ -162,7 +162,7 @@ impl Book {
}
hands.push(hand);
}
new_records.push(Record::new(rec.get_initial(), &hands, last_score.unwrap()));
new_records.push(Record::new(rec.get_initial(), &hands, last_score));
}
new_records.dedup();
Book::from_records(&new_records)
Expand Down Expand Up @@ -242,7 +242,7 @@ fn play_with_book<Eval: Evaluator>(
hands.push(hand);
board = board.play_hand(hand).unwrap();
}
let record = Record::new(Board::initial_state(), &hands, board.score().into());
let record = Record::new(Board::initial_state(), &hands, Some(board.score().into()));
eprintln!("{}", record);
book.lock().unwrap().append(record).unwrap();
}
Expand Down
8 changes: 1 addition & 7 deletions src/play.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use crate::engine::search::*;
use crate::engine::table::*;
use crate::engine::think::*;
use crate::setup::*;
use crate::train::*;
use clap::ArgMatches;
use rand::prelude::*;
use rayon::prelude::*;
Expand Down Expand Up @@ -377,11 +376,6 @@ pub fn codingame(_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>
solve_with_move(board.board, &mut solve_obj, &sub_solver, None)
};
solve_obj.cache_gen += 1;
match best {
Hand::Play(pos) => {
println!("{}", pos_to_str(pos).to_ascii_lowercase());
}
_ => panic!(),
}
println!("{}", format!("{}", best).to_ascii_lowercase());
}
}
120 changes: 97 additions & 23 deletions src/record.rs
Original file line number Diff line number Diff line change
@@ -1,54 +1,55 @@
#[cfg(test)]
mod test;
use crate::engine::board::*;
use crate::engine::hand::*;
use anyhow::Result;
use std::fmt::*;
use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::path::Path;
use std::str::FromStr;
use thiserror::Error;

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Record {
initial_board: Board,
hands: Vec<Hand>,
final_score: i16,
final_score: Option<i16>,
}

#[derive(Error, Debug)]
#[error("Score is not registered")]
pub struct ScoreIsNotRegistered {}

#[derive(Error, Debug)]
pub enum ParseRecordError {
#[error("Failed to parse hand")]
FailedToParseHand,
#[error("invalid hand")]
InvalidHand,
}

impl Record {
pub fn new(initial_board: Board, hands: &[Hand], final_score: i16) -> Record {
pub fn new(initial_board: Board, hands: &[Hand], final_score: Option<i16>) -> Record {
Record {
initial_board,
hands: hands.to_vec(),
final_score,
}
}

pub fn parse(record_str: &str) -> Result<Record> {
let mut hands = Vec::new();
let mut board = Board::initial_state();
let splitted = record_str.split_ascii_whitespace().collect::<Vec<_>>();
let l = splitted[0].len();
for i in 0..(l / 2) {
let h = Hand::from_str(&record_str[(2 * i)..(2 * i + 2)])?;
hands.push(h);
board = board.play_hand(h).ok_or(UnmovableError {})?;
}
let score = if let Some(score) = splitted.get(1) {
score.parse().unwrap()
} else {
board.score() as i16
};
Ok(Record::new(Board::initial_state(), &hands, score))
}

pub fn get_initial(&self) -> Board {
self.initial_board
}

pub fn timeline(&self) -> Result<Vec<(Board, Hand, i16)>> {
let mut board = self.initial_board;
let mut res = Vec::new();
let final_score = self.final_score.ok_or(ScoreIsNotRegistered {})?;
let mut score = if self.hands.len() % 2 == 0 {
self.final_score
final_score
} else {
-self.final_score
-final_score
};
for &h in &self.hands {
res.push((board, h, score));
Expand All @@ -65,7 +66,80 @@ impl Display for Record {
for h in &self.hands {
write!(f, "{}", h)?;
}
write!(f, " {}", self.final_score)?;
if let Some(final_score) = self.final_score {
write!(f, " {}", final_score)?;
}
Ok(())
}
}

impl FromStr for Record {
type Err = ParseRecordError;

fn from_str(record_str: &str) -> Result<Self, Self::Err> {
let mut hands = Vec::new();
let mut board = Board::initial_state();
let splitted = record_str.split_ascii_whitespace().collect::<Vec<_>>();
let l = splitted[0].len();
for i in 0..(l / 2) {
let h = splitted[0][(2 * i)..(2 * i + 2)]
.parse::<Hand>()
.or(Err(ParseRecordError::FailedToParseHand))?;
board = match board.play_hand(h) {
Some(next) => next,
None => {
let passed = board.pass().ok_or(ParseRecordError::InvalidHand)?;
match passed.play_hand(h) {
Some(next) => {
hands.push(Hand::Pass);
next
}
None => return Err(ParseRecordError::InvalidHand.into()),
}
}
};
hands.push(h);
}
let score = if let Some(score) = splitted.get(1) {
score.parse().ok()
} else if board.is_gameover() {
Some(board.score() as i16)
} else {
None
};
Ok(Record::new(Board::initial_state(), &hands, score))
}
}

pub struct LoadRecords<R: Read> {
reader: BufReader<R>,
buffer: String,
remain: usize,
}

impl<R: Read> Iterator for LoadRecords<R> {
type Item = Result<Record>;
fn next(&mut self) -> Option<Self::Item> {
if self.remain > 0 {
self.remain -= 1;
self.reader.read_line(&mut self.buffer).ok()?;
return Some(self.buffer.parse::<Record>().map_err(|e| e.into()));
}
None
}
}

pub fn load_records(path: &Path) -> Result<LoadRecords<File>> {
let file = File::open(path)?;
let mut reader = BufReader::new(file);
let mut buffer = String::new();

reader.read_line(&mut buffer)?;
let remain = buffer.trim().parse()?;

Ok(LoadRecords {
reader,
buffer,
remain,
})
}
22 changes: 22 additions & 0 deletions src/record/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
extern crate test;
use super::*;

#[test]
fn test_parse_record() {
let record = "f5d6c3d3 0".parse::<Record>().unwrap();
assert_eq!(record.initial_board, Board::initial_state());
let timeline = record.timeline().unwrap();
assert_eq!(timeline.len(), 5);
assert_eq!(timeline[0].1, "f5".parse::<Hand>().unwrap());
assert_eq!(timeline[1].1, "d6".parse::<Hand>().unwrap());
assert_eq!(timeline[2].1, "c3".parse::<Hand>().unwrap());
assert_eq!(timeline[3].1, "d3".parse::<Hand>().unwrap());
assert_eq!(timeline[4].1, Hand::Pass);
}

#[test]
fn test_parse_record_with_pass() {
let record_with_pass = "f5f6d3g5h5h4f7h6psc5 0".parse::<Record>().unwrap();
let record_without_pass = "f5f6d3g5h5h4f7h6c5 0".parse::<Record>().unwrap();
assert_eq!(record_with_pass, record_without_pass);
}
Loading
Loading