khetai 0.3.2 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|