Skip to content

Commit 145a676

Browse files
Merge pull request #73 from DeveloperPaul123/feature/code-coverage
feat: rework just file and add code coverage generation bench: 760228
2 parents fd369f8 + 9242770 commit 145a676

File tree

7 files changed

+161
-48
lines changed

7 files changed

+161
-48
lines changed

.github/workflows/build_and_test.yml

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,28 @@ name: Build and test
22

33
on:
44
push:
5-
branches: [ "main" ]
5+
branches: ["main"]
66
pull_request:
7-
branches: [ "main" ]
7+
branches: ["main"]
88

99
env:
1010
CARGO_TERM_COLOR: always
1111

1212
jobs:
13-
build-nightly:
14-
13+
build:
14+
strategy:
15+
matrix:
16+
runs-on: [ubuntu-22.04]
17+
rust: [stable, nightly]
18+
build-type: [debug, release]
1519
runs-on: ubuntu-22.04
16-
17-
steps:
18-
- name: Rust nightly
19-
run: rustup default stable
20-
- uses: actions/checkout@v4
21-
with:
22-
lfs: true
23-
- name: Install just
24-
uses: extractions/setup-just@v2
25-
- name: Run tests
26-
run: just test
27-
28-
build-stable:
29-
30-
runs-on: ubuntu-22.04
31-
3220
steps:
33-
- name: Rust stable
34-
run: rustup default stable
35-
- uses: actions/checkout@v4
36-
with:
37-
lfs: true
38-
- name: Install just
39-
uses: extractions/setup-just@v2
40-
- name: Run tests
41-
run: just test
21+
- name: Rust ${{ matrix.rust }}
22+
run: rustup default ${{ matrix.rust }}
23+
- uses: actions/checkout@v4
24+
with:
25+
lfs: true
26+
- name: Install just
27+
uses: extractions/setup-just@v2
28+
- name: Run tests
29+
run: just test ${{ matrix.build-type }}

.github/workflows/coverage.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Code coverage
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
pull_request:
7+
branches: ["main"]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
12+
jobs:
13+
generate-coverage:
14+
runs-on: ubuntu-24.04
15+
steps:
16+
- name: Rust nightly
17+
run: |
18+
rustup default nightly
19+
rustup component add llvm-tools-preview
20+
- uses: actions/checkout@v4
21+
with:
22+
lfs: true
23+
- name: Install just
24+
uses: extractions/setup-just@v2
25+
- name: Install grcov
26+
run: |
27+
sudo apt update
28+
sudo apt install -y grcov
29+
- name: Install just
30+
uses: extractions/setup-just@v2
31+
- name: Generate coverage
32+
run: just coverage
33+
- name: Upload coverage reports to Codecov
34+
uses: codecov/codecov-action@v5
35+
with:
36+
token: ${{ secrets.CODECOV_TOKEN }}
37+
files: target/debug/coverage/byte-knight.lcov

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@
22
/data/lichess_db_puzzle.csv
33
**/build/*
44
*.exe
5-
**/.DS_Store
5+
**/.DS_Store
6+
*.profraw
7+
*.profdata
8+
*.lcov

Justfile

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,103 @@
11
set windows-shell := ["pwsh.exe", "-NoLogo", "-Command"]
22

3-
default: build
3+
default: (build)
44

5-
build:
5+
[group('dev')]
6+
[doc('Build the project (default is debug)')]
7+
build config="debug":
68
echo "Building the project..."
7-
cargo build --release --all
9+
cargo build --workspace --all-features {{ if config=="release" {"--release"} else {""} }}
810

9-
test:
11+
[group('dev')]
12+
[doc('Build and run tests (default is debug)')]
13+
test config="debug":
1014
echo "Running tests..."
11-
cargo test --release --all --all-features
15+
cargo test --workspace --all-features {{ if config=="release" {"--release"} else {""} }} -- --include-ignored
1216

17+
export LLVM_PROFILE_FILE:="./target/coverage/byte_knight-%p-%m.profraw"
18+
19+
[group('dev')]
20+
[doc('Generate test coverage')]
21+
coverage: (build "debug")
22+
echo "Running tests with coverage..."
23+
mkdir -p target/coverage
24+
RUSTFLAGS="-Cinstrument-coverage" \
25+
cargo test --workspace -- --skip "perft"
26+
grcov target/coverage engine/target/coverage chess/target/coverage -s . \
27+
--binary-path ./target/debug/ --output-types lcov -o ./target/coverage/byte-knight.lcov \
28+
--branch --keep-only "src/*" --keep-only "engine/*" --keep-only "chess/*" \
29+
--ignore "src/bin/byte-knight/*" --ignore "chess/src/perft*"
30+
31+
[group('dev')]
32+
[doc('Purge files generated by @coverage')]
33+
purge-coverage:
34+
echo "Purging coverage data..."
35+
rm -rf *.profraw
36+
rm -rf target/coverage
37+
rm -rf chess/target
38+
rm -rf engine/target
39+
rm -rf chess/*.profraw
40+
rm -rf engine/*.profraw
41+
42+
[group('dev')]
43+
[doc('Run clippy')]
1344
lint:
1445
cargo clippy --all --all-features --tests -- -D warnings
1546

47+
[group('chess')]
48+
[group('performance')]
49+
[doc('Run sarch benchmark - required before committing for OpenBench.')]
1650
search-bench:
1751
echo "Running search benchmark..."
1852
cargo rustc --release --bin byte-knight -- -C target-cpu=native
1953
./target/release/byte-knight bench
2054

55+
[group('chess')]
56+
[doc('Run perft at a specified depth')]
2157
perft depth:
2258
echo "Running perft..."
2359
cargo run --release --bin perft -- -d {{ depth }}
2460

61+
[group('chess')]
62+
[doc('Run perft over the EPD test suite')]
2563
perft-epd:
2664
echo "Running EPD perft test suite..."
2765
cargo run --release --bin perft -- --epd-file data/standard.epd
2866

67+
[group('chess')]
68+
[group('performance')]
69+
[doc('Run perft benchmark over the EPD test suite')]
2970
perft-bench:
3071
echo "Running perft benchmark..."
3172
cargo run --release --bin perft-bench -- -e data/standard.epd
3273

74+
[group('chess')]
75+
[doc('Generate magic numbers use for magic bitboards')]
3376
magics:
3477
echo "Generating magics..."
3578
cargo run --release --bin generate_magics
3679

80+
[group('chess')]
81+
[doc('Verify that generated Zobrist hashes are unique')]
3782
verify-zobrist:
3883
echo "Verifying Zobrist hash..."
3984
cargo run --release --bin verify_zobrist
4085

86+
[group('dev')]
87+
[doc('Generate release binaries for given target.')]
4188
release target:
4289
echo "Building release binaries..."
4390
cargo rustc --release --bin byte-knight --target={{ target }}
4491

45-
cache-main: build
92+
[group('dev')]
93+
[doc('Caches the release binary to bk-main for testing.')]
94+
cache-main: (build "release")
4695
echo "Caching binary for testing..."
4796
cp target/release/byte-knight ./bk-main
4897

49-
compare-to-main engine1: build
98+
[group('dev')]
99+
[group('chess')]
100+
[doc('Run the engine against itself. Requires @cache-main to be run first and fastchess to be installed.')]
101+
compare-to-main engine1: (build "release")
50102
echo "Comparing {{ engine1 }} to bk-main"
51103
fastchess -engine cmd="{{ engine1 }}" name="dev" -engine cmd="./bk-main" name="bk-main" -openings file="./data/Pohl.epd" format=epd order=random -each tc=10+0.1 -rounds 200 -repeat -concurrency 8 -sprt elo0=0 elo1=5 alpha=0.05 beta=0.1 model=normalized -output format=cutechess

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<center><h1> byte-knight </h1></center>
22

3+
[![codecov](https://codecov.io/gh/DeveloperPaul123/byte-knight/graph/badge.svg?token=USEPKU8K4G)](https://codecov.io/gh/DeveloperPaul123/byte-knight)
4+
35
`byte-knight` is a UCI compliant chess engine written in Rust. It started as a port of the chess engine I submitted for Sebatian Lague's [Chess Engine Challenge](https://github.com/DeveloperPaul123/Leonidas) where it placed in the top 32 out of 600+ entries.
46

57
# Overview

engine/src/score.rs

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,25 @@ use crate::defs::MAX_DEPTH;
2424
pub type ScoreType = i16;
2525
pub(crate) type LargeScoreType = i32;
2626
/// Represents a score in centipawns.
27+
///
28+
/// This type has saturating add/sub operations to prevent overflow.
29+
/// It will not wrap around on overflow, but instead saturate to the internal types min/max.
30+
///
31+
/// The score is represented as a signed 16-bit integer, which allows for a range of -32,768 to 32,767.
32+
///
33+
/// Example usage:
34+
/// ```rust
35+
/// use engine::score::{Score, ScoreType};
36+
/// let score = Score::new(150); // Represents a score of 150 centipawns
37+
/// let mate_score = Score::MATE; // Represents a checkmate score
38+
/// let draw_score = Score::DRAW; // Represents a draw score
39+
/// let mut s = Score::INF / 2;
40+
/// s += Score::INF;
41+
/// assert_eq!(s, Score::INF); // Saturating addition
42+
/// let mut ss = -Score::INF;
43+
/// ss -= Score::INF;
44+
/// assert_eq!(ss, Score::new(ScoreType::MIN)); // Saturating subtraction
45+
/// ```
2746
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
2847
pub struct Score(pub ScoreType);
2948

@@ -83,55 +102,55 @@ impl Neg for Score {
83102

84103
impl AddAssign for Score {
85104
fn add_assign(&mut self, other: Score) {
86-
self.0 += other.0;
105+
*self = *self + other;
87106
}
88107
}
89108

90109
impl AddAssign<ScoreType> for Score {
91110
fn add_assign(&mut self, other: ScoreType) {
92-
self.0 += other;
111+
*self = *self + other;
93112
}
94113
}
95114

96115
impl Add for Score {
97116
type Output = Score;
98117

99-
fn add(self, other: Score) -> Score {
100-
Score(self.0 + other.0)
118+
fn add(self, other: Score) -> Self::Output {
119+
Score(self.0.saturating_add(other.0))
101120
}
102121
}
103122

104123
impl Add<ScoreType> for Score {
105124
type Output = Score;
106125

107-
fn add(self, other: ScoreType) -> Score {
108-
Score(self.0 + other)
126+
fn add(self, other: ScoreType) -> Self::Output {
127+
Score(self.0.saturating_add(other))
109128
}
110129
}
111130

112131
impl Sub for Score {
113132
type Output = Score;
114-
fn sub(self, other: Score) -> Score {
115-
Score(self.0 - other.0)
133+
fn sub(self, other: Score) -> Self::Output {
134+
Score(self.0.saturating_sub(other.0))
116135
}
117136
}
118137

119138
impl Sub<ScoreType> for Score {
120139
type Output = Score;
121140
fn sub(self, other: ScoreType) -> Score {
122-
Score(self.0 - other)
141+
Score(self.0.saturating_sub(other))
123142
}
124143
}
125144

126145
impl SubAssign for Score {
127146
fn sub_assign(&mut self, other: Score) {
128-
self.0 -= other.0;
147+
*self = *self - other;
129148
}
130149
}
131150

132151
impl SubAssign<ScoreType> for Score {
133152
fn sub_assign(&mut self, rhs: ScoreType) {
134-
self.0 -= rhs;
153+
*self = *self - rhs;
135154
}
136155
}
137156

@@ -193,3 +212,14 @@ impl Shl<u32> for Score {
193212
Score(self.0 << rhs)
194213
}
195214
}
215+
216+
#[cfg(test)]
217+
mod tests {
218+
use super::*;
219+
#[test]
220+
fn add_assign() {
221+
let mut right = Score::INF / 2;
222+
right += Score::INF;
223+
assert_eq!(right, Score::INF);
224+
}
225+
}

engine/src/search.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,7 @@ mod tests {
648648
}
649649

650650
#[test]
651+
#[ignore = "Timing on this is not consistent when instrumentation is enabled"]
651652
fn do_not_exceed_time() {
652653
let mut board = Board::default_board();
653654
let config = SearchParameters {

0 commit comments

Comments
 (0)