khetai 0.3.2 → 0.3.3
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 +29 -17
- 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: 947bf0b2701da92a96a4fd24d29740cbfa460614f71cfe927754ba37840aab1d
|
4
|
+
data.tar.gz: 56a86968143c90e83cb1996a0aa86bf81e3e7f876aec8b077b361d35a7fab7fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9bccf13dbbedce74f0a860a7fb0f09604caad0dc2fd395fe4fdd2484e7cd63f48c96ccaab076aeb1f0a6c75e3ca984dd85cf1973a8b348a949e8ba690c1af71
|
7
|
+
data.tar.gz: 7efc3618179af126593559ea32a7086f13e02c9ed8aaa509d01b8886f8eb12661e18ff0eb235754e609eacc161fb2a462a4e5162e877ba9b52c253f82104f6fb
|
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)) {
|
@@ -217,15 +217,21 @@ void make_move(Move move) {
|
|
217
217
|
// add starting piece to end location
|
218
218
|
hash ^= keys[board[end]][end];
|
219
219
|
|
220
|
-
enum Player moving_player = get_owner(moving_piece);
|
221
|
-
update_piece_tracker(moving_player, start, end);
|
222
|
-
|
223
220
|
// add ending piece to start location if swapping
|
224
221
|
if (is_piece(board[start])) {
|
225
222
|
hash ^= keys[board[start]][start];
|
226
223
|
|
227
224
|
enum Player other_player = get_owner(board[start]);
|
228
|
-
|
225
|
+
if (get_owner(moving_piece) == other_player)
|
226
|
+
update_piece_tracker(other_player, end, start, true);
|
227
|
+
else {
|
228
|
+
update_piece_tracker(moving_piece, start, end, false);
|
229
|
+
update_piece_tracker(other_player, end, start, false);
|
230
|
+
}
|
231
|
+
|
232
|
+
} else {
|
233
|
+
enum Player moving_player = get_owner(moving_piece);
|
234
|
+
update_piece_tracker(moving_player, start, end, false);
|
229
235
|
}
|
230
236
|
|
231
237
|
if (get_piece(moving_piece) == PHARAOH)
|
@@ -309,12 +315,18 @@ void undo_move() {
|
|
309
315
|
board[start] = board[end];
|
310
316
|
board[end] = moving_piece;
|
311
317
|
|
312
|
-
enum Player moving_player = get_owner(moving_piece);
|
313
|
-
update_piece_tracker(moving_player, start, end);
|
314
|
-
|
315
318
|
if (board[start] != 0) {
|
316
319
|
enum Player other_player = get_owner(board[start]);
|
317
|
-
|
320
|
+
|
321
|
+
if (get_owner(moving_piece) == other_player)
|
322
|
+
update_piece_tracker(other_player, end, start, true);
|
323
|
+
else {
|
324
|
+
update_piece_tracker(moving_piece, start, end, false);
|
325
|
+
update_piece_tracker(other_player, end, start, false);
|
326
|
+
}
|
327
|
+
} else {
|
328
|
+
enum Player moving_player = get_owner(moving_piece);
|
329
|
+
update_piece_tracker(moving_player, start, end, false);
|
318
330
|
}
|
319
331
|
|
320
332
|
if (get_piece(moving_piece) == PHARAOH)
|
@@ -326,7 +338,7 @@ void undo_move() {
|
|
326
338
|
void find_valid_moves(Move *valid_moves, int *vi) {
|
327
339
|
for (int i = 0; i < 13; i++) {
|
328
340
|
uint8_t board_pos = piece_trackers[whose_turn].positions[i];
|
329
|
-
if (board_pos !=
|
341
|
+
if (board_pos != EMPTY) {
|
330
342
|
enum Piece piece = get_piece(board[board_pos]);
|
331
343
|
switch (piece) {
|
332
344
|
case ANUBIS:
|
@@ -367,7 +379,7 @@ void find_valid_scarab_moves(int i, Move *valid_moves, int *vi) {
|
|
367
379
|
for (int j = 0; j < 8; j++) {
|
368
380
|
int dest = i + directions[j];
|
369
381
|
if (can_move[whose_turn][dest]) {
|
370
|
-
if (!is_piece(board[dest]) || (
|
382
|
+
if (!is_piece(board[dest]) || (get_piece(board[dest]) != SCARAB && get_piece(board[dest]) != PHARAOH)) {
|
371
383
|
valid_moves[(*vi)++] = new_move(i, dest, 0);
|
372
384
|
}
|
373
385
|
}
|
@@ -415,8 +427,8 @@ uint64_t get_board_hash() {
|
|
415
427
|
|
416
428
|
void init_piece_trackers() {
|
417
429
|
for (int i = 0; i < 13; i++) {
|
418
|
-
piece_trackers[SILVER].positions[i] =
|
419
|
-
piece_trackers[RED].positions[i] =
|
430
|
+
piece_trackers[SILVER].positions[i] = EMPTY;
|
431
|
+
piece_trackers[RED].positions[i] = EMPTY;
|
420
432
|
}
|
421
433
|
|
422
434
|
int si = 0;
|
@@ -435,8 +447,8 @@ void init_piece_trackers() {
|
|
435
447
|
ri++;
|
436
448
|
}
|
437
449
|
} else {
|
438
|
-
piece_trackers[SILVER].board_idx_position[i] =
|
439
|
-
piece_trackers[RED].board_idx_position[i] =
|
450
|
+
piece_trackers[SILVER].board_idx_position[i] = EMPTY;
|
451
|
+
piece_trackers[RED].board_idx_position[i] = EMPTY;
|
440
452
|
}
|
441
453
|
}
|
442
454
|
}
|
@@ -445,9 +457,9 @@ bool is_move_legal(Move move) {
|
|
445
457
|
int start = get_start(move);
|
446
458
|
int end = get_end(move);
|
447
459
|
if (is_piece(board[start]) && get_owner(board[start]) == whose_turn) {
|
448
|
-
if (!is_piece(board[end]) || get_rotation(move) != 0)
|
460
|
+
if (!is_piece(board[end]) || (get_rotation(move) != 0 && start == end))
|
449
461
|
return true;
|
450
|
-
else if (is_piece(board[end]) && get_piece(board[start]) == SCARAB && get_piece(board[end])
|
462
|
+
else if (is_piece(board[end]) && (get_piece(board[start]) == SCARAB && get_piece(board[end]) < 3))
|
451
463
|
return true;
|
452
464
|
}
|
453
465
|
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.3
|
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
|