khetai 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.tool-versions +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +13 -0
- data/ext/khetai/dev/fltk-ui/game_board.cpp +5 -1
- data/ext/khetai/dev/fltk-ui/game_board.h +1 -1
- data/ext/khetai/khetai_lib.c +49 -35
- data/ext/khetai/khetai_lib.h +19 -8
- data/lib/khetai/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0de4779baa3060126c239bcb22e4ed4cff09168f223d282b4653ee0b48a49c0
|
4
|
+
data.tar.gz: add3d7364b84a82e2268b2ae2e17ee3169a63e1191f7bbfefe036ffb9b727a64
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: adeeb8e3bd8605661ad1a3a7a58cdcae7728bafc6c1fb329f4ba602600e103c25adcfbfbdbfc29390a28272df04d4f15a92ea84c55034c5b1ed77e7b0d103249
|
7
|
+
data.tar.gz: 184800f1ddfc7db6d7366a29e611e8edf8149177423ce9236ee685b6ed7aa6c3b6ecb1faffd520d6da6a68041daee91c34b00d5800cefd63f1a23e50b7f9437e
|
data/.gitignore
CHANGED
data/.tool-versions
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby 3.3.6
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# KhetAI
|
2
2
|
|
3
|
+
An AI engine for the board game Khet. This gem provides move calculation for a specified color with configurable search depth and time limits.
|
4
|
+
|
3
5
|
## Installation
|
4
6
|
|
5
7
|
$ gem install khetai
|
@@ -45,8 +47,19 @@ The representation of the board gets converted an array of 8-bit integers once i
|
|
45
47
|
|
46
48
|
The internals of the gem are written in C, located in `ext/khetai`.
|
47
49
|
|
50
|
+
## Development System Requirements
|
51
|
+
|
52
|
+
To build the gem from source, you'll need:
|
53
|
+
- Ruby >= 2.0.0
|
54
|
+
- GCC or compatible C compiler
|
55
|
+
- Development headers (usually provided by ruby-dev or ruby-devel package)
|
56
|
+
|
48
57
|
## Build and Deploy Commands
|
49
58
|
|
59
|
+
This project uses `asdf` as the version manager for Ruby. However, any Ruby version greater than `2.0.0` should work to compile this gem.
|
60
|
+
|
61
|
+
$ gem install bundler
|
62
|
+
$ bundle install
|
50
63
|
$ bundle exec rake compile
|
51
64
|
$ bundle exec rake build
|
52
65
|
$ gem install pkg/khetai-<version>.gem
|
@@ -134,6 +134,11 @@ int GameBoard::handle(int event) {
|
|
134
134
|
if (key == 'r') {
|
135
135
|
resetPieces();
|
136
136
|
} else if (key == FL_Enter) {
|
137
|
+
square_selected = false;
|
138
|
+
square_selected_num = -1;
|
139
|
+
redraw();
|
140
|
+
Fl::flush();
|
141
|
+
|
137
142
|
const char *max_time_value = max_time_input->value();
|
138
143
|
if (!max_time_value || max_time_value[0] == '\0') {
|
139
144
|
max_time_input->value("5");
|
@@ -507,7 +512,6 @@ void GameBoard::updateLaserPosition() {
|
|
507
512
|
if (laser_y >= y() + (rows * cell_height) || laser_y <= y() || laser_x >= x() + (cols * cell_width) || laser_x <= x()) {
|
508
513
|
laser_active = false;
|
509
514
|
laser_path.clear();
|
510
|
-
redraw();
|
511
515
|
return;
|
512
516
|
}
|
513
517
|
|
@@ -65,7 +65,7 @@ class GameBoard : public Fl_Widget {
|
|
65
65
|
int cell_width, cell_height;
|
66
66
|
bool square_selected = false;
|
67
67
|
bool laser_active = false;
|
68
|
-
int laser_step =
|
68
|
+
int laser_step = 5;
|
69
69
|
LaserDirection laser_direction;
|
70
70
|
float laser_y, laser_x;
|
71
71
|
bool remove_piece = false;
|
data/ext/khetai/khetai_lib.c
CHANGED
@@ -154,7 +154,7 @@ int calculate_score() {
|
|
154
154
|
enum Player player = j;
|
155
155
|
for (int k = 0; k < 13; k++) {
|
156
156
|
int i = get_board_index(player, k);
|
157
|
-
if (i !=
|
157
|
+
if (i != EMPTY) {
|
158
158
|
Square s = board[i];
|
159
159
|
int value = 0;
|
160
160
|
switch (get_piece(s)) {
|
@@ -211,25 +211,32 @@ void make_move(Move move) {
|
|
211
211
|
if (is_piece(board[end]))
|
212
212
|
hash ^= keys[board[end]][end];
|
213
213
|
|
214
|
-
Square
|
215
|
-
|
216
|
-
board[
|
217
|
-
|
218
|
-
hash ^= keys[board[end]][end];
|
219
|
-
|
220
|
-
enum Player moving_player = get_owner(moving_piece);
|
221
|
-
update_piece_tracker(moving_player, start, end);
|
214
|
+
Square starting_square = board[start];
|
215
|
+
Square ending_square = board[end];
|
216
|
+
board[start] = ending_square;
|
217
|
+
board[end] = starting_square;
|
222
218
|
|
223
|
-
// add
|
224
|
-
|
225
|
-
hash ^= keys[board[start]][start];
|
219
|
+
// add starting piece to end location
|
220
|
+
hash ^= keys[starting_square][end];
|
226
221
|
|
227
|
-
|
228
|
-
|
222
|
+
enum Player moving_player = get_owner(starting_square);
|
223
|
+
if (!is_piece(ending_square)) {
|
224
|
+
update_piece_tracker(moving_player, start, end, false);
|
225
|
+
} else {
|
226
|
+
// add ending piece to start location if swapping
|
227
|
+
hash ^= keys[ending_square][start];
|
228
|
+
|
229
|
+
enum Player other_player = get_owner(ending_square);
|
230
|
+
if (get_owner(starting_square) == other_player)
|
231
|
+
update_piece_tracker(other_player, end, start, true);
|
232
|
+
else {
|
233
|
+
update_piece_tracker(moving_player, start, end, false);
|
234
|
+
update_piece_tracker(other_player, end, start, false);
|
235
|
+
}
|
229
236
|
}
|
230
237
|
|
231
|
-
if (get_piece(
|
232
|
-
pharaoh_loc[get_owner(
|
238
|
+
if (get_piece(starting_square) == PHARAOH)
|
239
|
+
pharaoh_loc[get_owner(starting_square)] = end;
|
233
240
|
}
|
234
241
|
|
235
242
|
undo_moves[undo_index] = new_move(end, start, -rotation);
|
@@ -305,20 +312,27 @@ void undo_move() {
|
|
305
312
|
if (rotation != 0) {
|
306
313
|
board[start] = rotate(board[start], rotation);
|
307
314
|
} else {
|
308
|
-
Square
|
309
|
-
|
310
|
-
board[
|
311
|
-
|
312
|
-
|
313
|
-
|
315
|
+
Square starting_square = board[start];
|
316
|
+
Square ending_square = board[end];
|
317
|
+
board[start] = ending_square;
|
318
|
+
board[end] = starting_square;
|
319
|
+
|
320
|
+
enum Player moving_player = get_owner(starting_square);
|
321
|
+
if (ending_square == 0) {
|
322
|
+
update_piece_tracker(moving_player, start, end, false);
|
323
|
+
} else {
|
324
|
+
enum Player other_player = get_owner(ending_square);
|
314
325
|
|
315
|
-
|
316
|
-
|
317
|
-
|
326
|
+
if (get_owner(starting_square) == other_player)
|
327
|
+
update_piece_tracker(other_player, end, start, true);
|
328
|
+
else {
|
329
|
+
update_piece_tracker(moving_player, start, end, false);
|
330
|
+
update_piece_tracker(other_player, end, start, false);
|
331
|
+
}
|
318
332
|
}
|
319
333
|
|
320
|
-
if (get_piece(
|
321
|
-
pharaoh_loc[get_owner(
|
334
|
+
if (get_piece(starting_square) == PHARAOH)
|
335
|
+
pharaoh_loc[get_owner(starting_square)] = end;
|
322
336
|
}
|
323
337
|
checkmate = false;
|
324
338
|
}
|
@@ -326,7 +340,7 @@ void undo_move() {
|
|
326
340
|
void find_valid_moves(Move *valid_moves, int *vi) {
|
327
341
|
for (int i = 0; i < 13; i++) {
|
328
342
|
uint8_t board_pos = piece_trackers[whose_turn].positions[i];
|
329
|
-
if (board_pos !=
|
343
|
+
if (board_pos != EMPTY) {
|
330
344
|
enum Piece piece = get_piece(board[board_pos]);
|
331
345
|
switch (piece) {
|
332
346
|
case ANUBIS:
|
@@ -367,7 +381,7 @@ void find_valid_scarab_moves(int i, Move *valid_moves, int *vi) {
|
|
367
381
|
for (int j = 0; j < 8; j++) {
|
368
382
|
int dest = i + directions[j];
|
369
383
|
if (can_move[whose_turn][dest]) {
|
370
|
-
if (!is_piece(board[dest]) || (
|
384
|
+
if (!is_piece(board[dest]) || (get_piece(board[dest]) != SCARAB && get_piece(board[dest]) != PHARAOH)) {
|
371
385
|
valid_moves[(*vi)++] = new_move(i, dest, 0);
|
372
386
|
}
|
373
387
|
}
|
@@ -415,8 +429,8 @@ uint64_t get_board_hash() {
|
|
415
429
|
|
416
430
|
void init_piece_trackers() {
|
417
431
|
for (int i = 0; i < 13; i++) {
|
418
|
-
piece_trackers[SILVER].positions[i] =
|
419
|
-
piece_trackers[RED].positions[i] =
|
432
|
+
piece_trackers[SILVER].positions[i] = EMPTY;
|
433
|
+
piece_trackers[RED].positions[i] = EMPTY;
|
420
434
|
}
|
421
435
|
|
422
436
|
int si = 0;
|
@@ -435,8 +449,8 @@ void init_piece_trackers() {
|
|
435
449
|
ri++;
|
436
450
|
}
|
437
451
|
} else {
|
438
|
-
piece_trackers[SILVER].board_idx_position[i] =
|
439
|
-
piece_trackers[RED].board_idx_position[i] =
|
452
|
+
piece_trackers[SILVER].board_idx_position[i] = EMPTY;
|
453
|
+
piece_trackers[RED].board_idx_position[i] = EMPTY;
|
440
454
|
}
|
441
455
|
}
|
442
456
|
}
|
@@ -445,9 +459,9 @@ bool is_move_legal(Move move) {
|
|
445
459
|
int start = get_start(move);
|
446
460
|
int end = get_end(move);
|
447
461
|
if (is_piece(board[start]) && get_owner(board[start]) == whose_turn) {
|
448
|
-
if (!is_piece(board[end]) || get_rotation(move) != 0)
|
462
|
+
if (!is_piece(board[end]) || (get_rotation(move) != 0 && start == end))
|
449
463
|
return true;
|
450
|
-
else if (is_piece(board[end]) && get_piece(board[start]) == SCARAB && get_piece(board[end])
|
464
|
+
else if (is_piece(board[end]) && (get_piece(board[start]) == SCARAB && get_piece(board[end]) < 3))
|
451
465
|
return true;
|
452
466
|
}
|
453
467
|
return false;
|
data/ext/khetai/khetai_lib.h
CHANGED
@@ -182,7 +182,10 @@ typedef struct HashEntry {
|
|
182
182
|
extern HashEntry table[TABLE_SIZE];
|
183
183
|
static inline HashEntry *search_table(uint64_t key) { return &table[key & TABLE_MASK]; };
|
184
184
|
|
185
|
-
#define
|
185
|
+
#define NO_SWAP 0
|
186
|
+
#define SWAP_SAME 1
|
187
|
+
#define SWAP_OTHER 2
|
188
|
+
#define EMPTY 0xFF
|
186
189
|
typedef struct PieceTracker {
|
187
190
|
uint8_t positions[13];
|
188
191
|
uint8_t board_idx_position[120];
|
@@ -191,20 +194,28 @@ extern PieceTracker piece_trackers[2];
|
|
191
194
|
|
192
195
|
static inline uint8_t get_board_index(enum Player player, uint8_t pos_idx) { return piece_trackers[player].positions[pos_idx]; }
|
193
196
|
static inline uint8_t get_position_index(enum Player player, uint8_t board_idx) { return piece_trackers[player].board_idx_position[board_idx]; }
|
194
|
-
static inline void update_piece_tracker(enum Player player, uint8_t old_board_idx, uint8_t new_board_idx) {
|
197
|
+
static inline void update_piece_tracker(enum Player player, uint8_t old_board_idx, uint8_t new_board_idx, bool swap) {
|
195
198
|
uint8_t pos_idx = get_position_index(player, old_board_idx);
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
+
if (!swap) {
|
200
|
+
piece_trackers[player].positions[pos_idx] = new_board_idx;
|
201
|
+
piece_trackers[player].board_idx_position[old_board_idx] = EMPTY;
|
202
|
+
piece_trackers[player].board_idx_position[new_board_idx] = pos_idx;
|
203
|
+
} else {
|
204
|
+
uint8_t other_pos_idx = get_position_index(player, new_board_idx);
|
205
|
+
piece_trackers[player].positions[pos_idx] = new_board_idx;
|
206
|
+
piece_trackers[player].positions[other_pos_idx] = old_board_idx;
|
207
|
+
piece_trackers[player].board_idx_position[new_board_idx] = pos_idx;
|
208
|
+
piece_trackers[player].board_idx_position[old_board_idx] = other_pos_idx;
|
209
|
+
}
|
199
210
|
}
|
200
211
|
static inline void remove_from_piece_tracker(enum Player player, uint8_t board_idx) {
|
201
212
|
uint8_t pos_idx = get_position_index(player, board_idx);
|
202
|
-
piece_trackers[player].positions[pos_idx] =
|
203
|
-
piece_trackers[player].board_idx_position[board_idx] =
|
213
|
+
piece_trackers[player].positions[pos_idx] = EMPTY;
|
214
|
+
piece_trackers[player].board_idx_position[board_idx] = EMPTY;
|
204
215
|
}
|
205
216
|
static inline void add_to_piece_tracker(enum Player player, uint8_t board_idx) {
|
206
217
|
uint8_t pos_idx = 0;
|
207
|
-
while (piece_trackers[player].positions[pos_idx] !=
|
218
|
+
while (piece_trackers[player].positions[pos_idx] != EMPTY)
|
208
219
|
pos_idx++;
|
209
220
|
piece_trackers[player].positions[pos_idx] = board_idx;
|
210
221
|
piece_trackers[player].board_idx_position[board_idx] = pos_idx;
|
data/lib/khetai/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: khetai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- jkugs
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-12-
|
11
|
+
date: 2024-12-15 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -20,6 +20,7 @@ extra_rdoc_files: []
|
|
20
20
|
files:
|
21
21
|
- ".clang-format"
|
22
22
|
- ".gitignore"
|
23
|
+
- ".tool-versions"
|
23
24
|
- Gemfile
|
24
25
|
- Gemfile.lock
|
25
26
|
- LICENSE.txt
|
@@ -105,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
106
|
- !ruby/object:Gem::Version
|
106
107
|
version: '0'
|
107
108
|
requirements: []
|
108
|
-
rubygems_version: 3.
|
109
|
+
rubygems_version: 3.5.22
|
109
110
|
signing_key:
|
110
111
|
specification_version: 4
|
111
112
|
summary: Khet AI
|