Skip to content

Commit

Permalink
Oxidation 0.7.2 changes:
Browse files Browse the repository at this point in the history
Improving heuristic for LMR (7 elo)
Remove pesto feature (retuning eval for standard chess gains just as much as it does)
Increase endgame elephant value (10 elo in variants with elephants)
RFP improving (7 elo)
Speed up chancellor movegen (15 elo VSTC in variants with chancellors)
Depth 4 futility pruning (7 elo)
Per phase advanced pawn bonus (4 elo)
Lower quiescence search depth (11 elo)
Eval tuning on new data (8 elo)
PV node LMP (4 elo)
Eval tuning (15 elo)

Bench: 2395054
  • Loading branch information
Mathmagician8191 committed Jul 29, 2024
1 parent 26367d9 commit 41d428e
Show file tree
Hide file tree
Showing 16 changed files with 424 additions and 586 deletions.
43 changes: 41 additions & 2 deletions liberty_chess/src/movegen.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::moves::Move;
use crate::{
Board, BISHOP, CAMEL, CENTAUR, CHAMPION, ELEPHANT, KING, KNIGHT, MANN, OBSTACLE, PAWN, ROOK,
WALL, ZEBRA,
Board, BISHOP, CAMEL, CENTAUR, CHAMPION, CHANCELLOR, ELEPHANT, KING, KNIGHT, MANN, OBSTACLE,
PAWN, ROOK, WALL, ZEBRA,
};

impl Board {
Expand Down Expand Up @@ -70,6 +70,19 @@ impl Board {
}
}
}
CHANCELLOR => {
for k in 0..self.height() {
self.add_if_legal(&mut boards, (i, j), (k, j), &mut skip_legality);
}
for l in 0..self.width() {
self.add_if_legal(&mut boards, (i, j), (i, l), &mut skip_legality);
}
for (k, l) in Self::jump_coords((i, j), 2, 1) {
if k < self.height() && l < self.width() {
self.add_if_legal(&mut boards, (i, j), (k, l), &mut skip_legality);
}
}
}
CAMEL => {
for (k, l) in Self::jump_coords((i, j), 3, 1) {
if k < self.height() && l < self.width() {
Expand Down Expand Up @@ -238,6 +251,19 @@ impl Board {
}
}
}
CHANCELLOR => {
for k in 0..self.height() {
self.add_if_pseudolegal(captures, quiets, (i, j), (k, j));
}
for l in 0..self.width() {
self.add_if_pseudolegal(captures, quiets, (i, j), (i, l));
}
for (k, l) in Self::jump_coords((i, j), 2, 1) {
if k < self.height() && l < self.width() {
self.add_if_pseudolegal(captures, quiets, (i, j), (k, l));
}
}
}
CAMEL => {
for (k, l) in Self::jump_coords((i, j), 3, 1) {
if k < self.height() && l < self.width() {
Expand Down Expand Up @@ -402,6 +428,19 @@ impl Board {
}
}
}
CHANCELLOR => {
for k in 0..self.height() {
self.add_if_pseudolegal_qsearch(&mut moves, (i, j), (k, j));
}
for l in 0..self.width() {
self.add_if_pseudolegal_qsearch(&mut moves, (i, j), (i, l));
}
for (k, l) in Self::jump_coords((i, j), 2, 1) {
if k < self.height() && l < self.width() {
self.add_if_pseudolegal_qsearch(&mut moves, (i, j), (k, l));
}
}
}
CAMEL => {
for (k, l) in Self::jump_coords((i, j), 3, 1) {
if k < self.height() && l < self.width() {
Expand Down
6 changes: 1 addition & 5 deletions liberty_chess_gui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -721,12 +721,8 @@ fn draw_menu(gui: &mut LibertyChessGUI, ctx: &Context, ui: &mut Ui) {
}
}
match player {
PlayerType::BuiltIn(ref mut qdepth, ref mut hash_size) => {
PlayerType::BuiltIn(ref mut hash_size) => {
if gui.config.get_advanced() {
ui.horizontal_top(|ui| {
ui.label("Quiescence depth");
raw_text_edit(ui, size * 2.0, qdepth);
});
ui.horizontal_top(|ui| {
ui.label("Hash size (MB)");
raw_text_edit(ui, size * 4.0, hash_size);
Expand Down
17 changes: 6 additions & 11 deletions liberty_chess_gui/src/players.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use liberty_chess::{Board, Gamestate, ALL_PIECES};
use oxidation::glue::process_position;
use oxidation::parameters::DEFAULT_PARAMETERS;
use oxidation::search::SEARCH_PARAMETERS;
use oxidation::{mvvlva_move, random_move, State, HASH_SIZE, MAX_QDEPTH, QDEPTH, VERSION_NUMBER};
use oxidation::{mvvlva_move, random_move, State, HASH_SIZE, VERSION_NUMBER};
use rand::{thread_rng, Rng};
use std::collections::HashMap;
use std::io::{BufReader, ErrorKind, Write};
Expand Down Expand Up @@ -184,8 +184,8 @@ pub struct Limits {
pub enum PlayerType {
RandomEngine,
MVVLVA,
// parameters are qdepth and hash size
BuiltIn(NumericalInput<u16>, NumericalInput<usize>),
// parameter is hash size
BuiltIn(NumericalInput<usize>),
External(String),
Multiplayer(String, NumericalInput<u16>, String),
}
Expand All @@ -195,7 +195,7 @@ impl ToString for PlayerType {
match self {
Self::RandomEngine => "Random Mover".to_owned(),
Self::MVVLVA => "MVVLVA".to_owned(),
Self::BuiltIn(..) => format!("Oxidation v{VERSION_NUMBER}"),
Self::BuiltIn(_) => format!("Oxidation v{VERSION_NUMBER}"),
Self::External(_) => "External engine (beta)".to_owned(),
Self::Multiplayer(..) => "Connect to server (beta)".to_owned(),
}
Expand All @@ -204,10 +204,7 @@ impl ToString for PlayerType {

impl PlayerType {
pub fn built_in() -> Self {
Self::BuiltIn(
NumericalInput::new(u16::from(QDEPTH), 0, MAX_QDEPTH),
NumericalInput::new(HASH_SIZE, 0, 1 << 32),
)
Self::BuiltIn(NumericalInput::new(HASH_SIZE, 0, 1 << 32))
}

#[cfg(feature = "clock")]
Expand Down Expand Up @@ -267,11 +264,10 @@ impl PlayerData {
match player {
PlayerType::RandomEngine => Ok(Self::RandomEngine),
PlayerType::MVVLVA => Ok(Self::MVVLVA),
PlayerType::BuiltIn(qdepth, hash_size) => {
PlayerType::BuiltIn(hash_size) => {
let (send_request, recieve_request) = channel();
let (send_result, recieve_result) = channel();
let hash_size = hash_size.get_value();
let qdepth = qdepth.get_value() as u8;
let (send_message, receive_message) = channel();
let ctx = ctx.clone();
spawn(move || {
Expand All @@ -287,7 +283,6 @@ impl PlayerData {
&receive_message,
board,
searchtime,
qdepth,
&mut state,
);
ctx.request_repaint();
Expand Down
3 changes: 1 addition & 2 deletions oxidation/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "oxidation"
version = "0.7.1"
version = "0.7.2"
authors.workspace = true
repository.workspace = true
license.workspace = true
Expand All @@ -26,5 +26,4 @@ ulci = {workspace = true}

[features]
default = []
pesto = []
feature_extraction = []
21 changes: 2 additions & 19 deletions oxidation/src/bin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ use oxidation::evaluate::evaluate;
use oxidation::parameters::DEFAULT_PARAMETERS;
use oxidation::search::SEARCH_PARAMETERS;
use oxidation::{
bench, divide, search, Output, SearchConfig, State, HASH_SIZE, MAX_QDEPTH, MULTI_PV_COUNT,
QDEPTH, QDEPTH_NAME, VERSION_NUMBER,
bench, divide, search, Output, SearchConfig, State, HASH_SIZE, MULTI_PV_COUNT, VERSION_NUMBER,
};
use std::collections::{HashMap, HashSet};
use std::io::{stdin, stdout, BufReader};
Expand Down Expand Up @@ -48,14 +47,6 @@ const BENCH_POSITIONS: &[(&str, i8)] = &[

fn startup_client(tx: &Sender<Message>) {
let mut options = HashMap::new();
options.insert(
QDEPTH_NAME.to_owned(),
UlciOption::Int(IntOption {
default: usize::from(QDEPTH),
min: 0,
max: usize::from(MAX_QDEPTH),
}),
);
options.insert(
HASH_NAME.to_owned(),
UlciOption::Int(IntOption {
Expand Down Expand Up @@ -100,7 +91,6 @@ fn startup_client(tx: &Sender<Message>) {
fn main() {
let (tx, rx) = channel();
spawn(move || startup_client(&tx));
let mut qdepth = QDEPTH;
let mut hash_size = HASH_SIZE;
let mut pv_lines = MULTI_PV_COUNT;
let mut position = get_startpos();
Expand All @@ -117,8 +107,7 @@ fn main() {
}
Message::Go(settings) => {
let searchmoves = settings.moves;
let mut settings =
SearchConfig::new_time(&position, &mut qdepth, settings.time, &rx, &mut debug);
let mut settings = SearchConfig::new_time(&position, settings.time, &rx, &mut debug);
let pv = search(
&mut state,
&mut settings,
Expand All @@ -136,10 +125,6 @@ fn main() {
println!("info error not currently searching");
}
Message::UpdateOption(name, value) => match &*name {
QDEPTH_NAME => match value {
OptionValue::UpdateInt(value) => qdepth = value as u8,
_ => println!("info error incorrect option type"),
},
HASH_NAME => match value {
OptionValue::UpdateInt(value) => {
if value != hash_size {
Expand Down Expand Up @@ -180,7 +165,6 @@ fn main() {
&mut state,
&mut board,
depth,
&mut qdepth,
&mut debug,
&rx,
Output::String(stdout()),
Expand All @@ -190,7 +174,6 @@ fn main() {
&mut state,
&mut board,
depth,
&mut qdepth,
&mut debug,
&rx,
Output::String(stdout()),
Expand Down
100 changes: 46 additions & 54 deletions oxidation/src/evaluate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,7 @@ use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign};
use ulci::Score;

#[cfg(not(feature = "feature_extraction"))]
use crate::parameters::{unpack_eg, unpack_mg, PackedParameters};

#[cfg(all(not(feature = "feature_extraction"), not(feature = "pesto")))]
use crate::parameters::pack;

#[cfg(all(feature = "pesto", not(feature = "feature_extraction")))]
use crate::pesto::PSQT;
use crate::parameters::{pack, unpack_eg, unpack_mg, PackedParameters};

/// Extracted evaluation features
#[derive(Clone)]
Expand All @@ -38,7 +32,7 @@ pub struct Features {
pub(crate) fn raw(
pieces: &Array2D<Piece>,
to_move: bool,
#[cfg(not(feature = "pesto"))] promotion_values: (i32, i32),
promotion_values: (i32, i32),
parameters: &PackedParameters,
) -> i32 {
let mut value = 0;
Expand All @@ -59,28 +53,11 @@ pub(crate) fn raw(
let mut piece_value = parameters.pieces[piece_type];
let mobility = Board::mobility(pieces, (i, j), piece);
piece_value += mobility * parameters.mobility_bonus[piece_type];
#[cfg(feature = "pesto")]
{
if height == 8 && width == 8 && piece_type < 6 {
let index = if piece > 0 { 7 - i } else { i };
piece_value += PSQT[piece_type][index][j];
} else {
let horizontal_distance = min(i, height - 1 - i).min(EDGE_DISTANCE);
let vertical_distance = min(j, width - 1 - j).min(EDGE_DISTANCE);
let index = INDEXING[horizontal_distance * (EDGE_DISTANCE + 1) + vertical_distance];
if index < EDGE_PARAMETER_COUNT {
piece_value -= parameters.edge_avoidance[piece_type][index];
}
}
}
#[cfg(not(feature = "pesto"))]
{
let horizontal_distance = min(i, height - 1 - i).min(EDGE_DISTANCE);
let vertical_distance = min(j, width - 1 - j).min(EDGE_DISTANCE);
let index = INDEXING[horizontal_distance * (EDGE_DISTANCE + 1) + vertical_distance];
if index < EDGE_PARAMETER_COUNT {
piece_value -= parameters.edge_avoidance[piece_type][index];
}
let horizontal_distance = min(i, height - 1 - i).min(EDGE_DISTANCE);
let vertical_distance = min(j, width - 1 - j).min(EDGE_DISTANCE);
let index = INDEXING[horizontal_distance * (EDGE_DISTANCE + 1) + vertical_distance];
if index < EDGE_PARAMETER_COUNT {
piece_value -= parameters.edge_avoidance[piece_type][index];
}
if pieces.get(block_i, j.wrapping_sub(1)) == enemy_pawn
|| pieces.get(block_i, j + 1) == enemy_pawn
Expand All @@ -105,16 +82,15 @@ pub(crate) fn raw(
}
}
// bonus for advanced pawn
#[cfg(not(feature = "pesto"))]
{
let squares_to_go = if piece > 0 { height - 1 - i } else { i } as i32;
if squares_to_go != 0 {
let divisor =
squares_to_go * parameters.pawn_scale_factor + parameters.pawn_scaling_bonus;
let mg_value = promotion_values.0 / divisor;
let eg_value = promotion_values.1 / divisor;
piece_value += pack(mg_value, eg_value);
}
let squares_to_go = if piece > 0 { height - 1 - i } else { i } as i32;
if squares_to_go != 0 {
let mg_divisor =
squares_to_go * parameters.mg_pawn_scale_factor + parameters.mg_pawn_scaling_bonus;
let eg_divisor =
squares_to_go * parameters.eg_pawn_scale_factor + parameters.eg_pawn_scaling_bonus;
let mg_value = promotion_values.0 / mg_divisor;
let eg_value = promotion_values.1 / eg_divisor;
piece_value += pack(mg_value, eg_value);
}
}
value += piece_value * multiplier;
Expand Down Expand Up @@ -188,10 +164,12 @@ pub fn eval_features<
}
for (squares_to_go, multiplier) in &features.pawn_list {
let multiplier = T::from(*multiplier);
let divisor =
T::from(*squares_to_go) * parameters.pawn_scale_factor + parameters.pawn_scaling_bonus;
middlegame += promotion_values.0 / divisor * multiplier;
endgame += promotion_values.1 / divisor * multiplier;
let mg_divisor =
T::from(*squares_to_go) * parameters.mg_pawn_scale_factor + parameters.mg_pawn_scaling_bonus;
let eg_divisor =
T::from(*squares_to_go) * parameters.eg_pawn_scale_factor + parameters.eg_pawn_scaling_bonus;
middlegame += promotion_values.0 / mg_divisor * multiplier;
endgame += promotion_values.1 / eg_divisor * multiplier;
}
let threshold = T::from(ENDGAME_THRESHOLD);
let material = T::from(features.material);
Expand Down Expand Up @@ -236,15 +214,28 @@ pub fn gradient(
let eg_pawn_attacked_penalty = features.attacked_by_pawn.map(|x| -f64::from(x) * eg_factor);
let mg_pawn_defended_bonus = features.defended_by_pawn.map(|x| f64::from(x) * mg_factor);
let eg_pawn_defended_bonus = features.defended_by_pawn.map(|x| f64::from(x) * eg_factor);
let mut pawn_scale_factor = 0.0;
let mut pawn_scaling_bonus = 0.0;
let piece_value = promotion_values.0 * mg_factor + promotion_values.1 * eg_factor;
let mut mg_pawn_scale_factor = 0.0;
let mut mg_pawn_scaling_bonus = 0.0;
let mut eg_pawn_scale_factor = 0.0;
let mut eg_pawn_scaling_bonus = 0.0;
for (squares, count) in &features.pawn_list {
let squares = f64::from(*squares);
let divisor = squares.mul_add(parameters.pawn_scale_factor, parameters.pawn_scaling_bonus);
let scaling_factor = -piece_value * f64::from(*count) / divisor.powi(2);
pawn_scale_factor += scaling_factor * squares;
pawn_scaling_bonus += scaling_factor;
let mg_divisor = squares.mul_add(
parameters.mg_pawn_scale_factor,
parameters.mg_pawn_scaling_bonus,
);
let eg_divisor = squares.mul_add(
parameters.eg_pawn_scale_factor,
parameters.eg_pawn_scaling_bonus,
);
let mg_scaling_factor =
-promotion_values.0 * mg_factor * f64::from(*count) / mg_divisor.powi(2);
let eg_scaling_factor =
-promotion_values.0 * eg_factor * f64::from(*count) / eg_divisor.powi(2);
mg_pawn_scale_factor += mg_scaling_factor * squares;
mg_pawn_scaling_bonus += mg_scaling_factor;
eg_pawn_scale_factor += eg_scaling_factor * squares;
eg_pawn_scaling_bonus += eg_scaling_factor;
}
Parameters {
pieces,
Expand All @@ -260,8 +251,10 @@ pub fn gradient(
eg_pawn_attacked_penalty,
mg_pawn_defended_bonus,
eg_pawn_defended_bonus,
pawn_scale_factor,
pawn_scaling_bonus,
mg_pawn_scale_factor,
mg_pawn_scaling_bonus,
eg_pawn_scale_factor,
eg_pawn_scaling_bonus,
}
}

Expand Down Expand Up @@ -353,7 +346,6 @@ pub fn evaluate(state: &State, board: &Board) -> i32 {
let score = raw(
board.board(),
board.to_move(),
#[cfg(not(feature = "pesto"))]
state.promotion_values,
&state.packed_parameters,
);
Expand Down
Loading

0 comments on commit 41d428e

Please sign in to comment.