mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 13:03:13 +02:00
fix: some linting issues
This commit is contained in:
parent
126bed59e8
commit
c1390d4c18
@ -1,6 +1,18 @@
|
||||
Checks: >
|
||||
clang-analyzer-*,bugprone-*,cert-*,concurrency-*,hicpp-*,misc-*,performance-*,
|
||||
portability-*,readability-*,clang-diagnostic-*,cppcoreguidelines-*
|
||||
WarningsAsErrors: '*'
|
||||
clang-analyzer-*,
|
||||
-clang-analyzer-security.*,
|
||||
bugprone-*,
|
||||
cert-err33-c,
|
||||
cert-err34-c,
|
||||
cert-fio38-c,
|
||||
performance-*,
|
||||
portability-*,
|
||||
misc-unused-parameters
|
||||
WarningsAsErrors: >
|
||||
clang-analyzer-*,
|
||||
bugprone-*,
|
||||
cert-err33-c,
|
||||
cert-err34-c,
|
||||
cert-fio38-c
|
||||
HeaderFilterRegex: '.*'
|
||||
FormatStyle: none
|
||||
|
||||
168
C/1dvelocitysimulator/main.c
Normal file
168
C/1dvelocitysimulator/main.c
Normal file
@ -0,0 +1,168 @@
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
#define LINE_LENGTH 100
|
||||
|
||||
void C()
|
||||
{
|
||||
printf("\nCheck\n");
|
||||
return;
|
||||
}
|
||||
|
||||
void printAcceleration(int acceleration)
|
||||
{
|
||||
printf("The value of acceleration is: %d\n", acceleration);
|
||||
system("PAUSE");
|
||||
return;
|
||||
}
|
||||
|
||||
void pauseSystem() { system("PAUSE"); }
|
||||
|
||||
void clearScreen()
|
||||
{
|
||||
system("CLS");
|
||||
return;
|
||||
}
|
||||
|
||||
void pauseForASecond()
|
||||
{
|
||||
Sleep(1000);
|
||||
return;
|
||||
}
|
||||
|
||||
void pauseForGivenTime(float given_time)
|
||||
{
|
||||
Sleep(fabs(given_time * 1000));
|
||||
return;
|
||||
}
|
||||
|
||||
float calculateVelocity(float starting_velocity, unsigned int physics_time, int *acceleration)
|
||||
{
|
||||
return (*acceleration) * physics_time + starting_velocity;
|
||||
}
|
||||
|
||||
int calculateDisplacement(float starting_velocity, int *acceleration, unsigned int physics_time)
|
||||
{
|
||||
return starting_velocity * physics_time + ((1 / 2) * (*acceleration) * (physics_time ^ 2));
|
||||
}
|
||||
|
||||
void printXPosition(int position)
|
||||
{
|
||||
printf("\nx position is: %d\n", position);
|
||||
return;
|
||||
}
|
||||
|
||||
void printClock(unsigned int *time)
|
||||
{
|
||||
printf("%d seconds passed\n", *time);
|
||||
return;
|
||||
}
|
||||
|
||||
float calculateStopTime(float velocity) { return 1 / velocity; }
|
||||
|
||||
void printLine(int position)
|
||||
{
|
||||
clearScreen();
|
||||
for (int i = -(LINE_LENGTH / 2); i < LINE_LENGTH / 2; i++)
|
||||
{
|
||||
if (i == position)
|
||||
printf("x");
|
||||
else
|
||||
printf("-");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void printVelocity(float velocity)
|
||||
{
|
||||
printf("Velocity is: %f\n", velocity);
|
||||
return;
|
||||
}
|
||||
|
||||
int calculateTimePassed(float velocity)
|
||||
{
|
||||
if (velocity >= 1 || velocity <= -1)
|
||||
return 1;
|
||||
else
|
||||
{
|
||||
printf("Time passed is: %f\n", fabs(1 / velocity));
|
||||
return fabs(1 / velocity);
|
||||
}
|
||||
}
|
||||
|
||||
void printAllInfo(int position, unsigned int *time, float *velocity)
|
||||
{
|
||||
pauseForGivenTime(calculateStopTime(*velocity));
|
||||
printLine(position);
|
||||
printXPosition(position);
|
||||
*time += calculateTimePassed(*velocity);
|
||||
printClock(time);
|
||||
printVelocity(*velocity);
|
||||
// pauseForASecond();
|
||||
return;
|
||||
}
|
||||
|
||||
float chooseVelocity()
|
||||
{
|
||||
float velocity;
|
||||
printf("Write velocity of the object in m / s: ");
|
||||
scanf("%f", &velocity);
|
||||
return velocity;
|
||||
}
|
||||
|
||||
int chooseAcceleration()
|
||||
{
|
||||
int acceleration;
|
||||
printf("Choose acceleration of the object in m / (s ^ 2):");
|
||||
scanf("%d", &acceleration);
|
||||
return acceleration;
|
||||
}
|
||||
|
||||
int outOfLine(int position)
|
||||
{
|
||||
if ((position < LINE_LENGTH / 2) && (position > -1 * (LINE_LENGTH / 2)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
void moveUntillOutOfLine(int position, unsigned int *time)
|
||||
{
|
||||
while (!outOfLine(position))
|
||||
{
|
||||
float velocity = chooseVelocity();
|
||||
float *Pvelocity = &velocity;
|
||||
position += calculateDisplacement(velocity, 0, 1);
|
||||
printAllInfo(position, time, Pvelocity);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void moveUntillOutOfVelocity(int position, int *acceleration, unsigned int *time)
|
||||
{
|
||||
float velocity = 0;
|
||||
float *Pvelocity = &velocity;
|
||||
while (!outOfLine(position))
|
||||
{
|
||||
position += calculateDisplacement(velocity, acceleration, 1);
|
||||
printXPosition(position);
|
||||
pauseSystem();
|
||||
velocity = calculateVelocity(velocity, 1, acceleration);
|
||||
printAllInfo(position, time, Pvelocity);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int position = 0, acceleration = -1;
|
||||
int *Pacceleration = &acceleration;
|
||||
unsigned int time = 0;
|
||||
unsigned int *Ptime = &time;
|
||||
moveUntillOutOfLine(position, Ptime);
|
||||
// moveUntillOutOfVelocity(position, Pacceleration, Ptime);
|
||||
return 0;
|
||||
}
|
||||
942
C/fps/main.c
942
C/fps/main.c
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,17 @@
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_error.h>
|
||||
#include <SDL2/SDL_events.h>
|
||||
#include <SDL2/SDL_image.h>
|
||||
#include <SDL2/SDL_keyboard.h>
|
||||
#include <SDL2/SDL_keycode.h>
|
||||
#include <SDL2/SDL_mouse.h>
|
||||
#include <SDL2/SDL_pixels.h>
|
||||
#include <SDL2/SDL_rect.h>
|
||||
#include <SDL2/SDL_render.h>
|
||||
#include <SDL2/SDL_stdinc.h>
|
||||
#include <SDL2/SDL_surface.h>
|
||||
#include <SDL2/SDL_timer.h>
|
||||
#include <SDL2/SDL_video.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -7,17 +19,16 @@
|
||||
#include <strings.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define WINDOW_WIDTH 800
|
||||
#define WINDOW_HEIGHT 600
|
||||
#define MAX_PATH_LEN 512
|
||||
#define MAX_FILES 1000
|
||||
enum { WINDOW_WIDTH = 800, WINDOW_HEIGHT = 600, MAX_PATH_LEN = 512, MAX_FILES = 1000 };
|
||||
|
||||
// Auto-navigation and rendering constants
|
||||
#define AUTO_NAV_INTERVAL_MS 100
|
||||
#define BACKGROUND_COLOR_R 32
|
||||
#define BACKGROUND_COLOR_G 32
|
||||
#define BACKGROUND_COLOR_B 32
|
||||
#define BACKGROUND_COLOR_A 255
|
||||
enum {
|
||||
AUTO_NAV_INTERVAL_MS = 100,
|
||||
BACKGROUND_COLOR_R = 32,
|
||||
BACKGROUND_COLOR_G = 32,
|
||||
BACKGROUND_COLOR_B = 32,
|
||||
BACKGROUND_COLOR_A = 255
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char **files;
|
||||
@ -71,7 +82,8 @@ static void handle_auto_navigation(ImageViewer *viewer);
|
||||
// Rotation/saving helpers
|
||||
static SDL_Surface *rotate_surface_90_cw(SDL_Surface *src);
|
||||
static SDL_Surface *rotate_surface_quarters(SDL_Surface *src, int quartersCW);
|
||||
static SDL_Surface *crop_surface_argb8888(SDL_Surface *src, int left, int top, int right, int bottom);
|
||||
static SDL_Surface *
|
||||
crop_surface_argb8888(SDL_Surface *src, int left, int top, int right, int bottom);
|
||||
static int save_processed_image(const ImageViewer *viewer);
|
||||
|
||||
// Safe memory copy wrapper to address static analyzer warnings
|
||||
@ -163,7 +175,7 @@ static int init_viewer(ImageViewer *viewer) {
|
||||
viewer->texture = NULL;
|
||||
viewer->original_surface = NULL;
|
||||
viewer->current_file[0] = '\0';
|
||||
viewer->zoom_factor = 1.0f;
|
||||
viewer->zoom_factor = 1.0F;
|
||||
viewer->trim_left = 0;
|
||||
viewer->trim_right = 0;
|
||||
viewer->trim_top = 0;
|
||||
@ -236,7 +248,7 @@ static int load_image(ImageViewer *viewer, const char *filename) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
viewer->zoom_factor = 1.0f;
|
||||
viewer->zoom_factor = 1.0F;
|
||||
// Reset trims on new image
|
||||
viewer->trim_left = 0;
|
||||
viewer->trim_right = 0;
|
||||
@ -246,7 +258,8 @@ static int load_image(ImageViewer *viewer, const char *filename) {
|
||||
viewer->offset_y = 0;
|
||||
viewer->rotation_degrees = 0; // reset rotation on new image
|
||||
|
||||
int window_w, window_h;
|
||||
int window_w;
|
||||
int window_h;
|
||||
SDL_GetWindowSize(viewer->window, &window_w, &window_h);
|
||||
|
||||
float scale_x = (float)window_w / viewer->image_width;
|
||||
@ -254,7 +267,7 @@ static int load_image(ImageViewer *viewer, const char *filename) {
|
||||
float auto_scale = (scale_x < scale_y) ? scale_x : scale_y;
|
||||
|
||||
// Only scale down if image is larger than window, never scale up
|
||||
if (auto_scale < 1.0f) {
|
||||
if (auto_scale < 1.0F) {
|
||||
viewer->zoom_factor = auto_scale;
|
||||
}
|
||||
|
||||
@ -263,7 +276,11 @@ static int load_image(ImageViewer *viewer, const char *filename) {
|
||||
}
|
||||
|
||||
static void render_image(ImageViewer *viewer) {
|
||||
SDL_SetRenderDrawColor(viewer->renderer, BACKGROUND_COLOR_R, BACKGROUND_COLOR_G, BACKGROUND_COLOR_B, BACKGROUND_COLOR_A);
|
||||
SDL_SetRenderDrawColor(viewer->renderer,
|
||||
BACKGROUND_COLOR_R,
|
||||
BACKGROUND_COLOR_G,
|
||||
BACKGROUND_COLOR_B,
|
||||
BACKGROUND_COLOR_A);
|
||||
SDL_RenderClear(viewer->renderer);
|
||||
|
||||
if (!viewer->texture) {
|
||||
@ -281,31 +298,50 @@ static void render_image(ImageViewer *viewer) {
|
||||
int bottom = viewer->trim_bottom < 0 ? 0 : viewer->trim_bottom;
|
||||
if (left + right >= base_w) {
|
||||
int excess = left + right - (base_w - 1);
|
||||
if (right >= excess) right -= excess; else left -= (excess - right);
|
||||
if (right >= excess) {
|
||||
right -= excess;
|
||||
} else {
|
||||
left -= (excess - right);
|
||||
}
|
||||
}
|
||||
if (top + bottom >= base_h) {
|
||||
int excess = top + bottom - (base_h - 1);
|
||||
if (bottom >= excess) bottom -= excess; else top -= (excess - bottom);
|
||||
if (bottom >= excess) {
|
||||
bottom -= excess;
|
||||
} else {
|
||||
top -= (excess - bottom);
|
||||
}
|
||||
}
|
||||
SDL_Rect src_rect;
|
||||
src_rect.x = left;
|
||||
src_rect.y = top;
|
||||
src_rect.w = base_w - left - right;
|
||||
src_rect.h = base_h - top - bottom;
|
||||
if (src_rect.w <= 0) src_rect.w = 1;
|
||||
if (src_rect.h <= 0) src_rect.h = 1;
|
||||
if (src_rect.w <= 0) {
|
||||
src_rect.w = 1;
|
||||
}
|
||||
if (src_rect.h <= 0) {
|
||||
src_rect.h = 1;
|
||||
}
|
||||
|
||||
int scaled_width = (int)(src_rect.w * viewer->zoom_factor);
|
||||
int scaled_height = (int)(src_rect.h * viewer->zoom_factor);
|
||||
|
||||
int window_w, window_h;
|
||||
int window_w;
|
||||
int window_h;
|
||||
SDL_GetWindowSize(viewer->window, &window_w, &window_h);
|
||||
|
||||
int x = (window_w - scaled_width) / 2 + viewer->offset_x;
|
||||
int y = (window_h - scaled_height) / 2 + viewer->offset_y;
|
||||
int x = ((window_w - scaled_width) / 2) + viewer->offset_x;
|
||||
int y = ((window_h - scaled_height) / 2) + viewer->offset_y;
|
||||
|
||||
SDL_Rect dest_rect = {x, y, scaled_width, scaled_height};
|
||||
SDL_RenderCopyEx(viewer->renderer, viewer->texture, &src_rect, &dest_rect, (double)viewer->rotation_degrees, NULL, SDL_FLIP_NONE);
|
||||
SDL_RenderCopyEx(viewer->renderer,
|
||||
viewer->texture,
|
||||
&src_rect,
|
||||
&dest_rect,
|
||||
(double)viewer->rotation_degrees,
|
||||
NULL,
|
||||
SDL_FLIP_NONE);
|
||||
|
||||
SDL_RenderPresent(viewer->renderer);
|
||||
}
|
||||
@ -314,26 +350,29 @@ static void handle_zoom(ImageViewer *viewer, float zoom_delta, int mouse_x, int
|
||||
float old_zoom = viewer->zoom_factor;
|
||||
viewer->zoom_factor += zoom_delta;
|
||||
|
||||
if (viewer->zoom_factor < 0.1f)
|
||||
viewer->zoom_factor = 0.1f;
|
||||
if (viewer->zoom_factor > 10.0f)
|
||||
viewer->zoom_factor = 10.0f;
|
||||
if (viewer->zoom_factor < 0.1F) {
|
||||
viewer->zoom_factor = 0.1F;
|
||||
}
|
||||
if (viewer->zoom_factor > 10.0F) {
|
||||
viewer->zoom_factor = 10.0F;
|
||||
}
|
||||
|
||||
float zoom_ratio = viewer->zoom_factor / old_zoom;
|
||||
|
||||
int window_w, window_h;
|
||||
int window_w;
|
||||
int window_h;
|
||||
SDL_GetWindowSize(viewer->window, &window_w, &window_h);
|
||||
|
||||
int center_x = window_w / 2;
|
||||
int center_y = window_h / 2;
|
||||
|
||||
viewer->offset_x =
|
||||
(viewer->offset_x - (mouse_x - center_x)) * zoom_ratio + (mouse_x - center_x);
|
||||
((viewer->offset_x - (mouse_x - center_x)) * zoom_ratio) + (mouse_x - center_x);
|
||||
viewer->offset_y =
|
||||
(viewer->offset_y - (mouse_y - center_y)) * zoom_ratio + (mouse_y - center_y);
|
||||
((viewer->offset_y - (mouse_y - center_y)) * zoom_ratio) + (mouse_y - center_y);
|
||||
}
|
||||
|
||||
static void print_help() {
|
||||
static void print_help(void) {
|
||||
printf("\n=== Image Viewer Controls ===\n");
|
||||
printf("Mouse wheel / +/-: Zoom in/out\n");
|
||||
printf("Mouse drag: Pan image\n");
|
||||
@ -372,8 +411,9 @@ static void cleanup_viewer(ImageViewer *viewer) {
|
||||
|
||||
static int is_image_file(const char *filename) {
|
||||
const char *ext = strrchr(filename, '.');
|
||||
if (!ext)
|
||||
if (!ext) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ext++; // Skip the dot
|
||||
return (strcasecmp(ext, "jpg") == 0 || strcasecmp(ext, "jpeg") == 0 ||
|
||||
@ -409,7 +449,7 @@ static int init_file_list(FileList *list, const char *path) {
|
||||
}
|
||||
|
||||
// First pass: count image files
|
||||
const struct dirent *entry;
|
||||
const struct dirent *entry = NULL;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (entry->d_name[0] != '.' && is_image_file(entry->d_name)) {
|
||||
// Build full path and check if it's a regular file
|
||||
@ -472,7 +512,8 @@ static int init_file_list(FileList *list, const char *path) {
|
||||
for (int i = 0; i < list->count - 1; i++) {
|
||||
for (int j = 0; j < list->count - i - 1; j++) {
|
||||
// Extract filenames without extensions
|
||||
char name1[MAX_PATH_LEN], name2[MAX_PATH_LEN];
|
||||
char name1[MAX_PATH_LEN];
|
||||
char name2[MAX_PATH_LEN];
|
||||
size_t len1 = strlen(list->files[j]);
|
||||
size_t len2 = strlen(list->files[j + 1]);
|
||||
|
||||
@ -483,10 +524,12 @@ static int init_file_list(FileList *list, const char *path) {
|
||||
|
||||
char *dot1 = strrchr(name1, '.');
|
||||
char *dot2 = strrchr(name2, '.');
|
||||
if (dot1)
|
||||
if (dot1) {
|
||||
*dot1 = '\0';
|
||||
if (dot2)
|
||||
}
|
||||
if (dot2) {
|
||||
*dot2 = '\0';
|
||||
}
|
||||
|
||||
// Custom comparison: shorter names first, then alphabetical
|
||||
int should_swap = 0;
|
||||
@ -520,7 +563,7 @@ static int init_file_list(FileList *list, const char *path) {
|
||||
|
||||
// Extract directory and filename
|
||||
char *last_slash = strrchr(path, '/');
|
||||
const char *target_filename;
|
||||
const char *target_filename = NULL;
|
||||
|
||||
if (last_slash) {
|
||||
size_t dir_len = last_slash - path;
|
||||
@ -545,7 +588,7 @@ static int init_file_list(FileList *list, const char *path) {
|
||||
}
|
||||
|
||||
// First pass: count image files in directory
|
||||
const struct dirent *entry;
|
||||
const struct dirent *entry = NULL;
|
||||
list->count = 0;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (entry->d_name[0] != '.' && is_image_file(entry->d_name)) {
|
||||
@ -611,7 +654,8 @@ static int init_file_list(FileList *list, const char *path) {
|
||||
for (int i = 0; i < list->count - 1; i++) {
|
||||
for (int j = 0; j < list->count - i - 1; j++) {
|
||||
// Extract filenames without extensions
|
||||
char name1[MAX_PATH_LEN], name2[MAX_PATH_LEN];
|
||||
char name1[MAX_PATH_LEN];
|
||||
char name2[MAX_PATH_LEN];
|
||||
size_t len1 = strlen(list->files[j]);
|
||||
size_t len2 = strlen(list->files[j + 1]);
|
||||
|
||||
@ -622,10 +666,12 @@ static int init_file_list(FileList *list, const char *path) {
|
||||
|
||||
char *dot1 = strrchr(name1, '.');
|
||||
char *dot2 = strrchr(name2, '.');
|
||||
if (dot1)
|
||||
if (dot1) {
|
||||
*dot1 = '\0';
|
||||
if (dot2)
|
||||
}
|
||||
if (dot2) {
|
||||
*dot2 = '\0';
|
||||
}
|
||||
|
||||
// Custom comparison: shorter names first, then alphabetical
|
||||
int should_swap = 0;
|
||||
@ -705,8 +751,9 @@ static int load_current_image(ImageViewer *viewer) {
|
||||
}
|
||||
|
||||
static int navigate_next_image(ImageViewer *viewer) {
|
||||
if (viewer->file_list.count <= 1)
|
||||
if (viewer->file_list.count <= 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
viewer->file_list.current_index =
|
||||
(viewer->file_list.current_index + 1) % viewer->file_list.count;
|
||||
@ -714,8 +761,9 @@ static int navigate_next_image(ImageViewer *viewer) {
|
||||
}
|
||||
|
||||
static int navigate_prev_image(ImageViewer *viewer) {
|
||||
if (viewer->file_list.count <= 1)
|
||||
if (viewer->file_list.count <= 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
viewer->file_list.current_index =
|
||||
(viewer->file_list.current_index - 1 + viewer->file_list.count) % viewer->file_list.count;
|
||||
@ -797,19 +845,24 @@ int main(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case SDLK_r:
|
||||
viewer.zoom_factor = 1.0f;
|
||||
viewer.zoom_factor = 1.0F;
|
||||
viewer.offset_x = 0;
|
||||
viewer.offset_y = 0;
|
||||
printf("Reset view\n");
|
||||
break;
|
||||
|
||||
case SDLK_f: {
|
||||
int window_w, window_h;
|
||||
int window_w;
|
||||
int window_h;
|
||||
SDL_GetWindowSize(viewer.window, &window_w, &window_h);
|
||||
int eff_w = viewer.image_width - viewer.trim_left - viewer.trim_right;
|
||||
int eff_h = viewer.image_height - viewer.trim_top - viewer.trim_bottom;
|
||||
if (eff_w < 1) eff_w = 1;
|
||||
if (eff_h < 1) eff_h = 1;
|
||||
if (eff_w < 1) {
|
||||
eff_w = 1;
|
||||
}
|
||||
if (eff_h < 1) {
|
||||
eff_h = 1;
|
||||
}
|
||||
float scale_x = (float)window_w / eff_w;
|
||||
float scale_y = (float)window_h / eff_h;
|
||||
viewer.zoom_factor = (scale_x < scale_y) ? scale_x : scale_y;
|
||||
@ -820,12 +873,12 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
case SDLK_PLUS:
|
||||
case SDLK_EQUALS:
|
||||
handle_zoom(&viewer, 0.1f, WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2);
|
||||
handle_zoom(&viewer, 0.1F, WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2);
|
||||
printf("Zoom: %.2f\n", viewer.zoom_factor);
|
||||
break;
|
||||
|
||||
case SDLK_MINUS:
|
||||
handle_zoom(&viewer, -0.1f, WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2);
|
||||
handle_zoom(&viewer, -0.1F, WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2);
|
||||
printf("Zoom: %.2f\n", viewer.zoom_factor);
|
||||
break;
|
||||
|
||||
@ -845,32 +898,42 @@ int main(int argc, char *argv[]) {
|
||||
int step = (SDL_GetModState() & KMOD_SHIFT) ? 50 : 10;
|
||||
int iw = viewer.image_width;
|
||||
int ih = viewer.image_height;
|
||||
if (iw <= 0 || ih <= 0) break;
|
||||
if (iw <= 0 || ih <= 0) {
|
||||
break;
|
||||
}
|
||||
switch (e.key.keysym.sym) {
|
||||
case SDLK_1: // left -
|
||||
viewer.trim_left -= step;
|
||||
if (viewer.trim_left < 0) viewer.trim_left = 0;
|
||||
if (viewer.trim_left < 0) {
|
||||
viewer.trim_left = 0;
|
||||
}
|
||||
break;
|
||||
case SDLK_2: // left +
|
||||
viewer.trim_left += step;
|
||||
break;
|
||||
case SDLK_3: // right -
|
||||
viewer.trim_right -= step;
|
||||
if (viewer.trim_right < 0) viewer.trim_right = 0;
|
||||
if (viewer.trim_right < 0) {
|
||||
viewer.trim_right = 0;
|
||||
}
|
||||
break;
|
||||
case SDLK_4: // right +
|
||||
viewer.trim_right += step;
|
||||
break;
|
||||
case SDLK_5: // top -
|
||||
viewer.trim_top -= step;
|
||||
if (viewer.trim_top < 0) viewer.trim_top = 0;
|
||||
if (viewer.trim_top < 0) {
|
||||
viewer.trim_top = 0;
|
||||
}
|
||||
break;
|
||||
case SDLK_6: // top +
|
||||
viewer.trim_top += step;
|
||||
break;
|
||||
case SDLK_7: // bottom -
|
||||
viewer.trim_bottom -= step;
|
||||
if (viewer.trim_bottom < 0) viewer.trim_bottom = 0;
|
||||
if (viewer.trim_bottom < 0) {
|
||||
viewer.trim_bottom = 0;
|
||||
}
|
||||
break;
|
||||
case SDLK_8: // bottom +
|
||||
viewer.trim_bottom += step;
|
||||
@ -879,38 +942,55 @@ int main(int argc, char *argv[]) {
|
||||
// Clamp so at least 1px remains
|
||||
if (viewer.trim_left + viewer.trim_right >= iw) {
|
||||
viewer.trim_right = iw - 1 - viewer.trim_left;
|
||||
if (viewer.trim_right < 0) viewer.trim_right = 0;
|
||||
if (viewer.trim_left >= iw) viewer.trim_left = iw - 1;
|
||||
if (viewer.trim_right < 0) {
|
||||
viewer.trim_right = 0;
|
||||
}
|
||||
if (viewer.trim_left >= iw) {
|
||||
viewer.trim_left = iw - 1;
|
||||
}
|
||||
}
|
||||
if (viewer.trim_top + viewer.trim_bottom >= ih) {
|
||||
viewer.trim_bottom = ih - 1 - viewer.trim_top;
|
||||
if (viewer.trim_bottom < 0) viewer.trim_bottom = 0;
|
||||
if (viewer.trim_top >= ih) viewer.trim_top = ih - 1;
|
||||
if (viewer.trim_bottom < 0) {
|
||||
viewer.trim_bottom = 0;
|
||||
}
|
||||
if (viewer.trim_top >= ih) {
|
||||
viewer.trim_top = ih - 1;
|
||||
}
|
||||
}
|
||||
int eff_w = iw - viewer.trim_left - viewer.trim_right;
|
||||
int eff_h = ih - viewer.trim_top - viewer.trim_bottom;
|
||||
printf("Trim L/R/T/B: %d/%d/%d/%d (effective %dx%d)\n",
|
||||
viewer.trim_left, viewer.trim_right, viewer.trim_top, viewer.trim_bottom,
|
||||
eff_w, eff_h);
|
||||
viewer.trim_left,
|
||||
viewer.trim_right,
|
||||
viewer.trim_top,
|
||||
viewer.trim_bottom,
|
||||
eff_w,
|
||||
eff_h);
|
||||
} break;
|
||||
|
||||
case SDLK_t: // reset trimming
|
||||
viewer.trim_left = viewer.trim_right = viewer.trim_top = viewer.trim_bottom = 0;
|
||||
viewer.trim_left = viewer.trim_right = viewer.trim_top =
|
||||
viewer.trim_bottom = 0;
|
||||
printf("Trims reset.\n");
|
||||
break;
|
||||
|
||||
case SDLK_LEFTBRACKET: { // '[' rotate left 90
|
||||
viewer.rotation_degrees -= 90;
|
||||
if (viewer.rotation_degrees <= -360)
|
||||
if (viewer.rotation_degrees <= -360) {
|
||||
viewer.rotation_degrees = 0;
|
||||
printf("Rotation: %d degrees\n", ((viewer.rotation_degrees%360)+360)%360);
|
||||
}
|
||||
printf("Rotation: %d degrees\n",
|
||||
((viewer.rotation_degrees % 360) + 360) % 360);
|
||||
} break;
|
||||
|
||||
case SDLK_RIGHTBRACKET: { // ']' rotate right 90
|
||||
viewer.rotation_degrees += 90;
|
||||
if (viewer.rotation_degrees >= 360)
|
||||
if (viewer.rotation_degrees >= 360) {
|
||||
viewer.rotation_degrees = 0;
|
||||
printf("Rotation: %d degrees\n", ((viewer.rotation_degrees%360)+360)%360);
|
||||
}
|
||||
printf("Rotation: %d degrees\n",
|
||||
((viewer.rotation_degrees % 360) + 360) % 360);
|
||||
} break;
|
||||
|
||||
case SDLK_s: {
|
||||
@ -959,9 +1039,10 @@ int main(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case SDL_MOUSEWHEEL: {
|
||||
int mouse_x, mouse_y;
|
||||
int mouse_x;
|
||||
int mouse_y;
|
||||
SDL_GetMouseState(&mouse_x, &mouse_y);
|
||||
float zoom_delta = e.wheel.y * 0.1f;
|
||||
float zoom_delta = e.wheel.y * 0.1F;
|
||||
handle_zoom(&viewer, zoom_delta, mouse_x, mouse_y);
|
||||
printf("Zoom: %.2f\n", viewer.zoom_factor);
|
||||
} break;
|
||||
@ -1002,17 +1083,21 @@ int main(int argc, char *argv[]) {
|
||||
int window_h = e.window.data2;
|
||||
int eff_w = viewer.image_width - viewer.trim_left - viewer.trim_right;
|
||||
int eff_h = viewer.image_height - viewer.trim_top - viewer.trim_bottom;
|
||||
if (eff_w < 1) eff_w = 1;
|
||||
if (eff_h < 1) eff_h = 1;
|
||||
if (eff_w < 1) {
|
||||
eff_w = 1;
|
||||
}
|
||||
if (eff_h < 1) {
|
||||
eff_h = 1;
|
||||
}
|
||||
float scale_x = (float)window_w / eff_w;
|
||||
float scale_y = (float)window_h / eff_h;
|
||||
float auto_scale = (scale_x < scale_y) ? scale_x : scale_y;
|
||||
|
||||
// Only scale down if image is larger than window, never scale up
|
||||
if (auto_scale < 1.0f) {
|
||||
if (auto_scale < 1.0F) {
|
||||
viewer.zoom_factor = auto_scale;
|
||||
} else {
|
||||
viewer.zoom_factor = 1.0f;
|
||||
viewer.zoom_factor = 1.0F;
|
||||
}
|
||||
|
||||
// Reset offset to center the image
|
||||
@ -1039,25 +1124,36 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// Rotate ARGB8888 surface 90 degrees clockwise
|
||||
static SDL_Surface *rotate_surface_90_cw(SDL_Surface *src) {
|
||||
if (!src) return NULL;
|
||||
if (!src) {
|
||||
return NULL;
|
||||
}
|
||||
int allocated_conv = 0;
|
||||
SDL_Surface *work = src;
|
||||
if (src->format->format != SDL_PIXELFORMAT_ARGB8888) {
|
||||
work = SDL_ConvertSurfaceFormat(src, SDL_PIXELFORMAT_ARGB8888, 0);
|
||||
if (!work) return NULL;
|
||||
if (!work) {
|
||||
return NULL;
|
||||
}
|
||||
allocated_conv = 1;
|
||||
}
|
||||
|
||||
int src_w = work->w;
|
||||
int src_h = work->h;
|
||||
SDL_Surface *dest = SDL_CreateRGBSurfaceWithFormat(0, src_h, src_w, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_Surface *dest =
|
||||
SDL_CreateRGBSurfaceWithFormat(0, src_h, src_w, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
if (!dest) {
|
||||
if (allocated_conv) SDL_FreeSurface(work);
|
||||
if (allocated_conv) {
|
||||
SDL_FreeSurface(work);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (SDL_MUSTLOCK(work)) SDL_LockSurface(work);
|
||||
if (SDL_MUSTLOCK(dest)) SDL_LockSurface(dest);
|
||||
if (SDL_MUSTLOCK(work)) {
|
||||
SDL_LockSurface(work);
|
||||
}
|
||||
if (SDL_MUSTLOCK(dest)) {
|
||||
SDL_LockSurface(dest);
|
||||
}
|
||||
|
||||
Uint32 *src_pixels = (Uint32 *)work->pixels;
|
||||
Uint32 *dst_pixels = (Uint32 *)dest->pixels;
|
||||
@ -1066,16 +1162,22 @@ static SDL_Surface *rotate_surface_90_cw(SDL_Surface *src) {
|
||||
|
||||
for (int y = 0; y < src_h; ++y) {
|
||||
for (int x = 0; x < src_w; ++x) {
|
||||
Uint32 pixel = src_pixels[y * src_pitch_px + x];
|
||||
Uint32 pixel = src_pixels[(y * src_pitch_px) + x];
|
||||
int nx = src_h - 1 - y;
|
||||
int ny = x;
|
||||
dst_pixels[ny * dst_pitch_px + nx] = pixel;
|
||||
dst_pixels[(ny * dst_pitch_px) + nx] = pixel;
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL_MUSTLOCK(dest)) SDL_UnlockSurface(dest);
|
||||
if (SDL_MUSTLOCK(work)) SDL_UnlockSurface(work);
|
||||
if (allocated_conv) SDL_FreeSurface(work);
|
||||
if (SDL_MUSTLOCK(dest)) {
|
||||
SDL_UnlockSurface(dest);
|
||||
}
|
||||
if (SDL_MUSTLOCK(work)) {
|
||||
SDL_UnlockSurface(work);
|
||||
}
|
||||
if (allocated_conv) {
|
||||
SDL_FreeSurface(work);
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
@ -1088,57 +1190,94 @@ static SDL_Surface *rotate_surface_quarters(SDL_Surface *src, int quartersCW) {
|
||||
}
|
||||
|
||||
SDL_Surface *current = SDL_ConvertSurfaceFormat(src, SDL_PIXELFORMAT_ARGB8888, 0);
|
||||
if (!current) return NULL;
|
||||
if (!current) {
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; i < quartersCW; ++i) {
|
||||
SDL_Surface *next = rotate_surface_90_cw(current);
|
||||
SDL_FreeSurface(current);
|
||||
if (!next) return NULL;
|
||||
if (!next) {
|
||||
return NULL;
|
||||
}
|
||||
current = next;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
// Crop ARGB8888 surface by trimming pixels from each side; returns new surface
|
||||
static SDL_Surface *crop_surface_argb8888(SDL_Surface *src, int left, int top, int right, int bottom) {
|
||||
if (!src) return NULL;
|
||||
static SDL_Surface *
|
||||
crop_surface_argb8888(SDL_Surface *src, int left, int top, int right, int bottom) {
|
||||
if (!src) {
|
||||
return NULL;
|
||||
}
|
||||
SDL_Surface *work = src;
|
||||
int free_work = 0;
|
||||
if (src->format->format != SDL_PIXELFORMAT_ARGB8888) {
|
||||
work = SDL_ConvertSurfaceFormat(src, SDL_PIXELFORMAT_ARGB8888, 0);
|
||||
if (!work) return NULL;
|
||||
if (!work) {
|
||||
return NULL;
|
||||
}
|
||||
free_work = 1;
|
||||
}
|
||||
|
||||
int iw = work->w;
|
||||
int ih = work->h;
|
||||
if (left < 0) left = 0;
|
||||
if (right < 0) right = 0;
|
||||
if (top < 0) top = 0;
|
||||
if (bottom < 0) bottom = 0;
|
||||
if (left + right >= iw) right = iw - 1 - left;
|
||||
if (top + bottom >= ih) bottom = ih - 1 - top;
|
||||
if (left < 0) {
|
||||
left = 0;
|
||||
}
|
||||
if (right < 0) {
|
||||
right = 0;
|
||||
}
|
||||
if (top < 0) {
|
||||
top = 0;
|
||||
}
|
||||
if (bottom < 0) {
|
||||
bottom = 0;
|
||||
}
|
||||
if (left + right >= iw) {
|
||||
right = iw - 1 - left;
|
||||
}
|
||||
if (top + bottom >= ih) {
|
||||
bottom = ih - 1 - top;
|
||||
}
|
||||
int cw = iw - left - right;
|
||||
int ch = ih - top - bottom;
|
||||
if (cw < 1) cw = 1;
|
||||
if (ch < 1) ch = 1;
|
||||
if (cw < 1) {
|
||||
cw = 1;
|
||||
}
|
||||
if (ch < 1) {
|
||||
ch = 1;
|
||||
}
|
||||
|
||||
SDL_Surface *out = SDL_CreateRGBSurfaceWithFormat(0, cw, ch, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
if (!out) {
|
||||
if (free_work) SDL_FreeSurface(work);
|
||||
if (free_work) {
|
||||
SDL_FreeSurface(work);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
if (SDL_MUSTLOCK(work)) SDL_LockSurface(work);
|
||||
if (SDL_MUSTLOCK(out)) SDL_LockSurface(out);
|
||||
if (SDL_MUSTLOCK(work)) {
|
||||
SDL_LockSurface(work);
|
||||
}
|
||||
if (SDL_MUSTLOCK(out)) {
|
||||
SDL_LockSurface(out);
|
||||
}
|
||||
Uint32 *sp = (Uint32 *)work->pixels;
|
||||
Uint32 *dp = (Uint32 *)out->pixels;
|
||||
int sp_pitch = work->pitch / 4;
|
||||
int dp_pitch = out->pitch / 4;
|
||||
for (int y = 0; y < ch; ++y) {
|
||||
memcpy(&dp[y * dp_pitch], &sp[(y + top) * sp_pitch + left], (size_t)cw * 4);
|
||||
memcpy(&dp[y * dp_pitch], &sp[((y + top) * sp_pitch) + left], (size_t)cw * 4);
|
||||
}
|
||||
if (SDL_MUSTLOCK(out)) {
|
||||
SDL_UnlockSurface(out);
|
||||
}
|
||||
if (SDL_MUSTLOCK(work)) {
|
||||
SDL_UnlockSurface(work);
|
||||
}
|
||||
if (free_work) {
|
||||
SDL_FreeSurface(work);
|
||||
}
|
||||
if (SDL_MUSTLOCK(out)) SDL_UnlockSurface(out);
|
||||
if (SDL_MUSTLOCK(work)) SDL_UnlockSurface(work);
|
||||
if (free_work) SDL_FreeSurface(work);
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -1149,9 +1288,11 @@ static int save_processed_image(const ImageViewer *viewer) {
|
||||
}
|
||||
|
||||
// First, crop based on current trims (before rotation to match on-screen behavior)
|
||||
SDL_Surface *cropped = crop_surface_argb8888(
|
||||
viewer->original_surface,
|
||||
viewer->trim_left, viewer->trim_top, viewer->trim_right, viewer->trim_bottom);
|
||||
SDL_Surface *cropped = crop_surface_argb8888(viewer->original_surface,
|
||||
viewer->trim_left,
|
||||
viewer->trim_top,
|
||||
viewer->trim_right,
|
||||
viewer->trim_bottom);
|
||||
if (!cropped) {
|
||||
printf("Failed to crop surface for saving.\n");
|
||||
return 0;
|
||||
@ -1189,10 +1330,14 @@ static int save_processed_image(const ImageViewer *viewer) {
|
||||
if (ext_ptr && *(ext_ptr + 1) != '\0') {
|
||||
ext_ptr++; // skip dot
|
||||
size_t eLen = strlen(ext_ptr);
|
||||
if (eLen >= sizeof(ext_lower)) eLen = sizeof(ext_lower) - 1;
|
||||
if (eLen >= sizeof(ext_lower)) {
|
||||
eLen = sizeof(ext_lower) - 1;
|
||||
}
|
||||
for (size_t i = 0; i < eLen; ++i) {
|
||||
char c = ext_ptr[i];
|
||||
if (c >= 'A' && c <= 'Z') c = (char)(c - 'A' + 'a');
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
c = (char)(c - 'A' + 'a');
|
||||
}
|
||||
ext_lower[i] = c;
|
||||
}
|
||||
} else {
|
||||
@ -1202,7 +1347,9 @@ static int save_processed_image(const ImageViewer *viewer) {
|
||||
|
||||
// Trim name_wo_ext at last dot to remove extension
|
||||
char *dot = strrchr(name_wo_ext, '.');
|
||||
if (dot) *dot = '\0';
|
||||
if (dot) {
|
||||
*dot = '\0';
|
||||
}
|
||||
|
||||
char out_path[MAX_PATH_LEN * 2];
|
||||
char fname[MAX_PATH_LEN];
|
||||
@ -1210,32 +1357,50 @@ static int save_processed_image(const ImageViewer *viewer) {
|
||||
// Decide saving function by extension; fallback to png if unsupported
|
||||
int saved = 0;
|
||||
int fallback_png = 0;
|
||||
int any_trim = (viewer->trim_left | viewer->trim_right | viewer->trim_top | viewer->trim_bottom) != 0;
|
||||
int any_trim =
|
||||
(viewer->trim_left | viewer->trim_right | viewer->trim_top | viewer->trim_bottom) != 0;
|
||||
|
||||
if (strcmp(ext_lower, "png") == 0) {
|
||||
int n = snprintf(fname, sizeof fname, "%s_%s.png", name_wo_ext, any_trim ? "trimmed" : "rotated");
|
||||
int n = snprintf(
|
||||
fname, sizeof fname, "%s_%s.png", name_wo_ext, any_trim ? "trimmed" : "rotated");
|
||||
if (n >= 0 && (size_t)n < sizeof fname &&
|
||||
safe_format_path(out_path, sizeof out_path, viewer->file_list.base_dir, fname)) {
|
||||
if (IMG_SavePNG(save_surf, out_path) == 0) saved = 1;
|
||||
if (IMG_SavePNG(save_surf, out_path) == 0) {
|
||||
saved = 1;
|
||||
}
|
||||
}
|
||||
} else if (strcmp(ext_lower, "jpg") == 0 || strcmp(ext_lower, "jpeg") == 0) {
|
||||
int n = snprintf(fname, sizeof fname, "%s_%s.%s", name_wo_ext, any_trim ? "trimmed" : "rotated", ext_lower);
|
||||
int n = snprintf(fname,
|
||||
sizeof fname,
|
||||
"%s_%s.%s",
|
||||
name_wo_ext,
|
||||
any_trim ? "trimmed" : "rotated",
|
||||
ext_lower);
|
||||
if (n >= 0 && (size_t)n < sizeof fname &&
|
||||
safe_format_path(out_path, sizeof out_path, viewer->file_list.base_dir, fname)) {
|
||||
if (IMG_SaveJPG(save_surf, out_path, 90) == 0) saved = 1;
|
||||
if (IMG_SaveJPG(save_surf, out_path, 90) == 0) {
|
||||
saved = 1;
|
||||
}
|
||||
}
|
||||
} else if (strcmp(ext_lower, "bmp") == 0) {
|
||||
int n = snprintf(fname, sizeof fname, "%s_%s.bmp", name_wo_ext, any_trim ? "trimmed" : "rotated");
|
||||
int n = snprintf(
|
||||
fname, sizeof fname, "%s_%s.bmp", name_wo_ext, any_trim ? "trimmed" : "rotated");
|
||||
if (n >= 0 && (size_t)n < sizeof fname &&
|
||||
safe_format_path(out_path, sizeof out_path, viewer->file_list.base_dir, fname)) {
|
||||
if (SDL_SaveBMP(save_surf, out_path) == 0) saved = 1;
|
||||
if (SDL_SaveBMP(save_surf, out_path) == 0) {
|
||||
saved = 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Unsupported original extension for saving -> fallback to PNG
|
||||
int n = snprintf(fname, sizeof fname, "%s_%s.png", name_wo_ext, any_trim ? "trimmed" : "rotated");
|
||||
int n = snprintf(
|
||||
fname, sizeof fname, "%s_%s.png", name_wo_ext, any_trim ? "trimmed" : "rotated");
|
||||
if (n >= 0 && (size_t)n < sizeof fname &&
|
||||
safe_format_path(out_path, sizeof out_path, viewer->file_list.base_dir, fname)) {
|
||||
if (IMG_SavePNG(save_surf, out_path) == 0) { saved = 1; fallback_png = 1; }
|
||||
if (IMG_SavePNG(save_surf, out_path) == 0) {
|
||||
saved = 1;
|
||||
fallback_png = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,8 @@
|
||||
// Contract expected by PYTHON/lichess_bot/engine.py:
|
||||
// - Usage without explanation: random_engine --fen "<FEN>" <uci1> <uci2> ...
|
||||
// -> prints the chosen UCI move on stdout
|
||||
// - With explanation: random_engine --fen "<FEN>" --explain [--analyze <uci>] <uci1> <uci2> ...
|
||||
// - With explanation: random_engine --fen "<FEN>" --explain [--analyze <uci>] <uci1>
|
||||
// <uci2> ...
|
||||
// -> prints a compact JSON object containing chosen_move and a simple analyze block
|
||||
//
|
||||
// Notes:
|
||||
@ -10,179 +11,239 @@
|
||||
// - We choose a uniformly random move among the provided UCIs.
|
||||
// - For "--analyze" the candidate score is a placeholder (0.0) for now.
|
||||
|
||||
#include "movegen.h"
|
||||
#include "search.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "movegen.h"
|
||||
#include "search.h"
|
||||
|
||||
typedef struct {
|
||||
const char *fen;
|
||||
int explain;
|
||||
const char *analyze_move;
|
||||
const char **moves;
|
||||
int move_count;
|
||||
typedef struct
|
||||
{
|
||||
const char *fen;
|
||||
int explain;
|
||||
const char *analyze_move;
|
||||
const char **moves;
|
||||
int move_count;
|
||||
} Args;
|
||||
|
||||
static void print_usage(const char *prog) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s --fen '<FEN>' [--explain] [--analyze <uci>] <uci_moves...>\n",
|
||||
prog);
|
||||
static void print_usage(const char *prog)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s --fen '<FEN>' [--explain] [--analyze <uci>] <uci_moves...>\n", prog);
|
||||
}
|
||||
|
||||
static int parse_args(int argc, char **argv, Args *out) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->moves = NULL;
|
||||
out->move_count = 0;
|
||||
static int parse_args(int argc, char **argv, Args *out)
|
||||
{
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->moves = NULL;
|
||||
out->move_count = 0;
|
||||
|
||||
// Collect options regardless of order; every non-option token is a move.
|
||||
const char **moves = NULL;
|
||||
int moves_cap = 0;
|
||||
int moves_len = 0;
|
||||
// Collect options regardless of order; every non-option token is a move.
|
||||
const char **moves = NULL;
|
||||
int moves_cap = 0;
|
||||
int moves_len = 0;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
const char *a = argv[i];
|
||||
if (strcmp(a, "--fen") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "--fen requires an argument\n");
|
||||
free(moves);
|
||||
return 0;
|
||||
}
|
||||
out->fen = argv[++i];
|
||||
continue;
|
||||
}
|
||||
if (strcmp(a, "--explain") == 0) {
|
||||
out->explain = 1;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(a, "--analyze") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "--analyze requires a UCI move\n");
|
||||
free(moves);
|
||||
return 0;
|
||||
}
|
||||
out->analyze_move = argv[++i];
|
||||
continue;
|
||||
}
|
||||
// Otherwise treat as move
|
||||
if (moves_len >= moves_cap) {
|
||||
int new_cap = moves_cap == 0 ? 8 : moves_cap * 2;
|
||||
const char **tmp = (const char**)realloc(moves, (size_t)new_cap * sizeof(const char*));
|
||||
if (!tmp) {
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
free(moves);
|
||||
return 0;
|
||||
}
|
||||
moves = tmp;
|
||||
moves_cap = new_cap;
|
||||
}
|
||||
moves[moves_len++] = a;
|
||||
}
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
const char *a = argv[i];
|
||||
if (strcmp(a, "--fen") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
{
|
||||
fprintf(stderr, "--fen requires an argument\n");
|
||||
free(moves);
|
||||
return 0;
|
||||
}
|
||||
out->fen = argv[++i];
|
||||
continue;
|
||||
}
|
||||
if (strcmp(a, "--explain") == 0)
|
||||
{
|
||||
out->explain = 1;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(a, "--analyze") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
{
|
||||
fprintf(stderr, "--analyze requires a UCI move\n");
|
||||
free(moves);
|
||||
return 0;
|
||||
}
|
||||
out->analyze_move = argv[++i];
|
||||
continue;
|
||||
}
|
||||
// Otherwise treat as move
|
||||
if (moves_len >= moves_cap)
|
||||
{
|
||||
int new_cap = moves_cap == 0 ? 8 : moves_cap * 2;
|
||||
const char **tmp =
|
||||
(const char **)realloc(moves, (size_t)new_cap * sizeof(const char *));
|
||||
if (!tmp)
|
||||
{
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
free(moves);
|
||||
return 0;
|
||||
}
|
||||
moves = tmp;
|
||||
moves_cap = new_cap;
|
||||
}
|
||||
moves[moves_len++] = a;
|
||||
}
|
||||
|
||||
if (!out->fen) {
|
||||
fprintf(stderr, "Missing --fen argument\n");
|
||||
free(moves);
|
||||
return 0;
|
||||
}
|
||||
if (!out->fen)
|
||||
{
|
||||
fprintf(stderr, "Missing --fen argument\n");
|
||||
free(moves);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (moves_len > 0) {
|
||||
out->moves = moves; // keep ownership until program end
|
||||
out->move_count = moves_len;
|
||||
} else {
|
||||
free(moves);
|
||||
out->moves = NULL;
|
||||
out->move_count = 0;
|
||||
}
|
||||
return 1;
|
||||
if (moves_len > 0)
|
||||
{
|
||||
out->moves = moves; // keep ownership until program end
|
||||
out->move_count = moves_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(moves);
|
||||
out->moves = NULL;
|
||||
out->move_count = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pick_random_index(int n, const char *fen) {
|
||||
if (n <= 0) return -1;
|
||||
// Mix in time and a simple FEN hash for a touch of variety/repeatability.
|
||||
unsigned long hash = 1469598103934665603ULL; // FNV offset basis
|
||||
if (fen) {
|
||||
const unsigned char *p = (const unsigned char*)fen;
|
||||
while (*p) {
|
||||
hash ^= (unsigned long)(*p++);
|
||||
hash *= 1099511628211ULL; // FNV prime
|
||||
}
|
||||
}
|
||||
unsigned long seed = (unsigned long)time(NULL) ^ hash;
|
||||
srand((unsigned int)(seed ^ (seed >> 32)));
|
||||
int idx = rand() % n;
|
||||
return idx;
|
||||
static int pick_random_index(int n, const char *fen)
|
||||
{
|
||||
if (n <= 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// Mix in time and a simple FEN hash for a touch of variety/repeatability.
|
||||
unsigned long hash = 1469598103934665603ULL; // FNV offset basis
|
||||
if (fen)
|
||||
{
|
||||
const unsigned char *p = (const unsigned char *)fen;
|
||||
while (*p)
|
||||
{
|
||||
hash ^= (unsigned long)(*p++);
|
||||
hash *= 1099511628211ULL; // FNV prime
|
||||
}
|
||||
}
|
||||
unsigned long seed = (unsigned long)time(NULL) ^ hash;
|
||||
srand((unsigned int)(seed ^ (seed >> 32)));
|
||||
int idx = rand() % n;
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int find_best_move_from_ucis(const char **ucis, int n_ucis, const char *fen, int depth, int *out_index){
|
||||
Position pos;
|
||||
if (!parse_fen(&pos, fen)) return 0;
|
||||
// Convert UCI list into legal moves vetted by our generator, but preserve provided order as fallback
|
||||
Move legal[256]; int map_idx[256]; int L=0;
|
||||
for (int i=0;i<n_ucis;i++){
|
||||
Move m; if (move_from_uci(&pos, ucis[i], &m)){ legal[L] = m; map_idx[L] = i; L++; }
|
||||
}
|
||||
if (L==0){ return 0; }
|
||||
int best_idx = 0; int best_score = -2147483647; int bf=-1, bt=-1;
|
||||
for (int i=0;i<L;i++){
|
||||
Position child = pos; Piece cap=EMPTY; make_move(&child, &legal[i], &cap);
|
||||
int sf=-1, st=-1;
|
||||
int score = -alphabeta(child, depth-1, -30000, 30000, &sf, &st);
|
||||
if (score > best_score){ best_score = score; best_idx = i; bf = legal[i].from; bt = legal[i].to; }
|
||||
}
|
||||
// Map best move back to index in original ucis list using map_idx
|
||||
if (out_index) *out_index = map_idx[best_idx];
|
||||
return 1;
|
||||
static int find_best_move_from_ucis(const char **ucis, int n_ucis, const char *fen, int depth,
|
||||
int *out_index)
|
||||
{
|
||||
Position pos;
|
||||
if (!parse_fen(&pos, fen))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// Convert UCI list into legal moves vetted by our generator, but preserve provided order as
|
||||
// fallback
|
||||
Move legal[256];
|
||||
int map_idx[256];
|
||||
int L = 0;
|
||||
for (int i = 0; i < n_ucis; i++)
|
||||
{
|
||||
Move m;
|
||||
if (move_from_uci(&pos, ucis[i], &m))
|
||||
{
|
||||
legal[L] = m;
|
||||
map_idx[L] = i;
|
||||
L++;
|
||||
}
|
||||
}
|
||||
if (L == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int best_idx = 0;
|
||||
int best_score = -2147483647;
|
||||
for (int i = 0; i < L; i++)
|
||||
{
|
||||
Position child = pos;
|
||||
Piece cap = EMPTY;
|
||||
make_move(&child, &legal[i], &cap);
|
||||
PrincipalVariation pv = {.from = -1, .to = -1};
|
||||
int score = -alphabeta(child, depth - 1, -30000, 30000, &pv);
|
||||
if (score > best_score)
|
||||
{
|
||||
best_score = score;
|
||||
best_idx = i;
|
||||
}
|
||||
}
|
||||
// Map best move back to index in original ucis list using map_idx
|
||||
if (out_index)
|
||||
{
|
||||
*out_index = map_idx[best_idx];
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
Args args;
|
||||
if (!parse_args(argc, argv, &args)) {
|
||||
print_usage(argv[0]);
|
||||
return 2;
|
||||
}
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
Args args;
|
||||
if (!parse_args(argc, argv, &args))
|
||||
{
|
||||
print_usage(argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (args.move_count <= 0) {
|
||||
// No legal moves provided; output nothing to keep contract simple.
|
||||
if (args.explain) {
|
||||
// Still return a valid JSON object for callers expecting it.
|
||||
printf("{\"chosen_index\":-1,\"chosen_move\":\"\",\"analyze\":{\"candidate_move\":\"%s\",\"candidate_score\":0.0}}\n",
|
||||
args.analyze_move ? args.analyze_move : "");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (args.move_count <= 0)
|
||||
{
|
||||
// No legal moves provided; output nothing to keep contract simple.
|
||||
if (args.explain)
|
||||
{
|
||||
// Still return a valid JSON object for callers expecting it.
|
||||
printf("{\"chosen_index\":-1,\"chosen_move\":\"\",\"analyze\":{\"candidate_move\":\"%"
|
||||
"s\",\"candidate_score\":0.0}}\n",
|
||||
args.analyze_move ? args.analyze_move : "");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If we have a FEN and move list, run a shallow alpha-beta to choose among provided moves.
|
||||
int chosen_idx = -1;
|
||||
if (args.fen && args.move_count>0){
|
||||
if (!find_best_move_from_ucis(args.moves, args.move_count, args.fen, 3, &chosen_idx)){
|
||||
chosen_idx = pick_random_index(args.move_count, args.fen);
|
||||
}
|
||||
} else {
|
||||
chosen_idx = pick_random_index(args.move_count, args.fen);
|
||||
}
|
||||
if (chosen_idx < 0 || chosen_idx >= args.move_count) {
|
||||
fprintf(stderr, "Internal error picking move index\n");
|
||||
return 1;
|
||||
}
|
||||
const char *chosen = args.moves[chosen_idx];
|
||||
// If we have a FEN and move list, run a shallow alpha-beta to choose among provided moves.
|
||||
int chosen_idx = -1;
|
||||
if (args.fen && args.move_count > 0)
|
||||
{
|
||||
if (!find_best_move_from_ucis(args.moves, args.move_count, args.fen, 3, &chosen_idx))
|
||||
{
|
||||
chosen_idx = pick_random_index(args.move_count, args.fen);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
chosen_idx = pick_random_index(args.move_count, args.fen);
|
||||
}
|
||||
if (chosen_idx < 0 || chosen_idx >= args.move_count)
|
||||
{
|
||||
(void)fprintf(stderr, "Internal error picking move index\n");
|
||||
return 1;
|
||||
}
|
||||
const char *chosen = args.moves[chosen_idx];
|
||||
|
||||
if (!args.explain) {
|
||||
printf("%s\n", chosen);
|
||||
return 0;
|
||||
}
|
||||
if (!args.explain)
|
||||
{
|
||||
printf("%s\n", chosen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Minimal JSON explanation compatible with engine.py's parser
|
||||
// Fields consumed by Python wrapper:
|
||||
// - chosen_move (string)
|
||||
// - chosen_index (int)
|
||||
// - analyze.candidate_score (number) [optional but provided]
|
||||
// Additionally include analyze.candidate_move for easier debugging.
|
||||
const char *cand = args.analyze_move ? args.analyze_move : "";
|
||||
double cand_score = 0.0; // placeholder; real eval will come later
|
||||
// Minimal JSON explanation compatible with engine.py's parser
|
||||
// Fields consumed by Python wrapper:
|
||||
// - chosen_move (string)
|
||||
// - chosen_index (int)
|
||||
// - analyze.candidate_score (number) [optional but provided]
|
||||
// Additionally include analyze.candidate_move for easier debugging.
|
||||
const char *cand = args.analyze_move ? args.analyze_move : "";
|
||||
double cand_score = 0.0; // placeholder; real eval will come later
|
||||
|
||||
printf("{\"chosen_index\":%d,\"chosen_move\":\"%s\",\"analyze\":{\"candidate_move\":\"%s\",\"candidate_score\":%.1f}}\n",
|
||||
chosen_idx, chosen, cand, cand_score);
|
||||
return 0;
|
||||
printf("{\"chosen_index\":%d,\"chosen_move\":\"%s\",\"analyze\":{\"candidate_move\":\"%s\","
|
||||
"\"candidate_score\":%.1f}}\n",
|
||||
chosen_idx, chosen, cand, cand_score);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -1,287 +1,750 @@
|
||||
// Readable micro-Max (https://home.hccnet.nl/h.g.muller/max-src2.html) inspired engine: CLI-compatible with engine.py
|
||||
// Usage:
|
||||
// Readable micro-Max (https://home.hccnet.nl/h.g.muller/max-src2.html) inspired engine:
|
||||
// CLI-compatible with engine.py Usage:
|
||||
// micro_max_engine [--seed N] [--fen FEN] [--explain] [--analyze UCI] <move1> <move2> ...
|
||||
// Behavior: ranks provided UCI moves using a simple material/king-safety heuristic derived
|
||||
// from the FEN position (if given). Prints chosen move by default, or JSON with --explain.
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
|
||||
typedef struct {
|
||||
char squares[64]; // a1=0 .. h8=63, '.' empty, uppercase white, lowercase black
|
||||
int white_to_move; // 1 white, 0 black
|
||||
typedef struct
|
||||
{
|
||||
char squares[64]; // a1=0 .. h8=63, '.' empty, uppercase white, lowercase black
|
||||
int white_to_move; // 1 white, 0 black
|
||||
} Board;
|
||||
|
||||
static int file_of(int idx) { return idx % 8; }
|
||||
static int rank_of(int idx) { return idx / 8; }
|
||||
static int idx_from_fr(int f, int r) { return r * 8 + f; }
|
||||
static int idx_from_fr(int f, int r) { return (r * 8) + f; }
|
||||
static int on_board(int f, int r) { return f >= 0 && f < 8 && r >= 0 && r < 8; }
|
||||
static int is_white(char p) { return p >= 'A' && p <= 'Z'; }
|
||||
static int is_black(char p) { return p >= 'a' && p <= 'z'; }
|
||||
|
||||
static int piece_value_cp(char p) {
|
||||
switch ((int)tolower((unsigned char)p)) {
|
||||
case 'p': return 100;
|
||||
case 'n': return 320;
|
||||
case 'b': return 330;
|
||||
case 'r': return 500;
|
||||
case 'q': return 900;
|
||||
case 'k': return 0;
|
||||
default: return 0;
|
||||
}
|
||||
static int piece_value_cp(char p)
|
||||
{
|
||||
switch (tolower((unsigned char)p))
|
||||
{
|
||||
case 'p':
|
||||
return 100;
|
||||
case 'n':
|
||||
return 320;
|
||||
case 'b':
|
||||
return 330;
|
||||
case 'r':
|
||||
return 500;
|
||||
case 'q':
|
||||
return 900;
|
||||
case 'k':
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_fen(Board *b, const char *fen) {
|
||||
memset(b->squares, '.', sizeof(b->squares));
|
||||
b->white_to_move = 1;
|
||||
if (!fen || !*fen) return 0;
|
||||
int f = 0, r = 7;
|
||||
const char *p = fen;
|
||||
while (*p && *p != ' ') {
|
||||
char c = *p++;
|
||||
if (c == '/') { f = 0; r--; if (r < 0) return 0; continue; }
|
||||
if (c >= '1' && c <= '8') { f += (c - '0'); if (f > 8) return 0; continue; }
|
||||
if (isalpha((unsigned char)c)) { if (f >= 8 || r < 0) return 0; b->squares[idx_from_fr(f, r)] = c; f++; }
|
||||
else return 0;
|
||||
}
|
||||
if (*p == ' ') p++;
|
||||
if (*p == 'w') { b->white_to_move = 1; p++; }
|
||||
else if (*p == 'b') { b->white_to_move = 0; p++; }
|
||||
return 1;
|
||||
static int parse_fen(Board *b, const char *fen)
|
||||
{
|
||||
memset(b->squares, '.', sizeof(b->squares));
|
||||
b->white_to_move = 1;
|
||||
if (!fen || !*fen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int f = 0;
|
||||
int r = 7;
|
||||
const char *p = fen;
|
||||
while (*p && *p != ' ')
|
||||
{
|
||||
char c = *p++;
|
||||
if (c == '/')
|
||||
{
|
||||
f = 0;
|
||||
r--;
|
||||
if (r < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (c >= '1' && c <= '8')
|
||||
{
|
||||
f += (c - '0');
|
||||
if (f > 8)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (isalpha((unsigned char)c))
|
||||
{
|
||||
if (f >= 8 || r < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
b->squares[idx_from_fr(f, r)] = c;
|
||||
f++;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (*p == ' ')
|
||||
{
|
||||
p++;
|
||||
}
|
||||
if (*p == 'w')
|
||||
{
|
||||
b->white_to_move = 1;
|
||||
p++;
|
||||
}
|
||||
else if (*p == 'b')
|
||||
{
|
||||
b->white_to_move = 0;
|
||||
p++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int find_king(const Board *b, int white) {
|
||||
char k = white ? 'K' : 'k';
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
if (b->squares[i] == k) return i;
|
||||
}
|
||||
return -1;
|
||||
static int find_king(const Board *b, int white)
|
||||
{
|
||||
char k = white ? 'K' : 'k';
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
if (b->squares[i] == k)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int sq_attacked_by(const Board *b, int target_idx, int by_white) {
|
||||
int tf = file_of(target_idx), tr = rank_of(target_idx);
|
||||
// Knights
|
||||
const int kdf[8] = {1,2, 2,1, -1,-2, -2,-1};
|
||||
const int kdr[8] = {2,1, -1,-2, 2,1, -1,-2};
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
int f = tf + kdf[i], r = tr + kdr[i];
|
||||
if (on_board(f, r)) {
|
||||
char p = b->squares[idx_from_fr(f, r)];
|
||||
if (by_white ? p == 'N' : p == 'n') return 1;
|
||||
}
|
||||
}
|
||||
// King
|
||||
for (int df = -1; df <= 1; ++df) for (int dr = -1; dr <= 1; ++dr) if (df || dr) {
|
||||
int f = tf + df, r = tr + dr;
|
||||
if (on_board(f, r)) {
|
||||
char p = b->squares[idx_from_fr(f, r)];
|
||||
if (by_white ? p == 'K' : p == 'k') return 1;
|
||||
}
|
||||
}
|
||||
// Pawns
|
||||
if (by_white) {
|
||||
int f1 = tf - 1, r1 = tr - 1;
|
||||
int f2 = tf + 1, r2 = tr - 1;
|
||||
if (on_board(f1, r1) && b->squares[idx_from_fr(f1, r1)] == 'P') return 1;
|
||||
if (on_board(f2, r2) && b->squares[idx_from_fr(f2, r2)] == 'P') return 1;
|
||||
} else {
|
||||
int f1 = tf - 1, r1 = tr + 1;
|
||||
int f2 = tf + 1, r2 = tr + 1;
|
||||
if (on_board(f1, r1) && b->squares[idx_from_fr(f1, r1)] == 'p') return 1;
|
||||
if (on_board(f2, r2) && b->squares[idx_from_fr(f2, r2)] == 'p') return 1;
|
||||
}
|
||||
// Bishops/queens
|
||||
const int dsf[4] = {1, 1, -1, -1};
|
||||
const int dsr[4] = {1, -1, 1, -1};
|
||||
for (int d = 0; d < 4; ++d) {
|
||||
int f = tf + dsf[d], r = tr + dsr[d];
|
||||
while (on_board(f, r)) {
|
||||
char p = b->squares[idx_from_fr(f, r)];
|
||||
if (p != '.') { if (by_white ? (p == 'B' || p == 'Q') : (p == 'b' || p == 'q')) return 1; break; }
|
||||
f += dsf[d]; r += dsr[d];
|
||||
}
|
||||
}
|
||||
// Rooks/queens
|
||||
const int rsf[4] = {1, -1, 0, 0};
|
||||
const int rsr[4] = {0, 0, 1, -1};
|
||||
for (int d = 0; d < 4; ++d) {
|
||||
int f = tf + rsf[d], r = tr + rsr[d];
|
||||
while (on_board(f, r)) {
|
||||
char p = b->squares[idx_from_fr(f, r)];
|
||||
if (p != '.') { if (by_white ? (p == 'R' || p == 'Q') : (p == 'r' || p == 'q')) return 1; break; }
|
||||
f += rsf[d]; r += rsr[d];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
static int sq_attacked_by(const Board *b, int target_idx, int by_white)
|
||||
{
|
||||
int tf = file_of(target_idx);
|
||||
int tr = rank_of(target_idx);
|
||||
// Knights
|
||||
const int kdf[8] = {1, 2, 2, 1, -1, -2, -2, -1};
|
||||
const int kdr[8] = {2, 1, -1, -2, 2, 1, -1, -2};
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
int f = tf + kdf[i];
|
||||
int r = tr + kdr[i];
|
||||
if (on_board(f, r))
|
||||
{
|
||||
char p = b->squares[idx_from_fr(f, r)];
|
||||
if (by_white ? p == 'N' : p == 'n')
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// King
|
||||
for (int df = -1; df <= 1; ++df)
|
||||
{
|
||||
for (int dr = -1; dr <= 1; ++dr)
|
||||
{
|
||||
if (df || dr)
|
||||
{
|
||||
int f = tf + df;
|
||||
int r = tr + dr;
|
||||
if (on_board(f, r))
|
||||
{
|
||||
char p = b->squares[idx_from_fr(f, r)];
|
||||
if (by_white ? p == 'K' : p == 'k')
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Pawns
|
||||
if (by_white)
|
||||
{
|
||||
int f1 = tf - 1;
|
||||
int r1 = tr - 1;
|
||||
int f2 = tf + 1;
|
||||
int r2 = tr - 1;
|
||||
if (on_board(f1, r1) && b->squares[idx_from_fr(f1, r1)] == 'P')
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (on_board(f2, r2) && b->squares[idx_from_fr(f2, r2)] == 'P')
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int f1 = tf - 1;
|
||||
int r1 = tr + 1;
|
||||
int f2 = tf + 1;
|
||||
int r2 = tr + 1;
|
||||
if (on_board(f1, r1) && b->squares[idx_from_fr(f1, r1)] == 'p')
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (on_board(f2, r2) && b->squares[idx_from_fr(f2, r2)] == 'p')
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// Bishops/queens
|
||||
const int dsf[4] = {1, 1, -1, -1};
|
||||
const int dsr[4] = {1, -1, 1, -1};
|
||||
for (int d = 0; d < 4; ++d)
|
||||
{
|
||||
int f = tf + dsf[d];
|
||||
int r = tr + dsr[d];
|
||||
while (on_board(f, r))
|
||||
{
|
||||
char p = b->squares[idx_from_fr(f, r)];
|
||||
if (p != '.')
|
||||
{
|
||||
if (by_white ? (p == 'B' || p == 'Q') : (p == 'b' || p == 'q'))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
f += dsf[d];
|
||||
r += dsr[d];
|
||||
}
|
||||
}
|
||||
// Rooks/queens
|
||||
const int rsf[4] = {1, -1, 0, 0};
|
||||
const int rsr[4] = {0, 0, 1, -1};
|
||||
for (int d = 0; d < 4; ++d)
|
||||
{
|
||||
int f = tf + rsf[d];
|
||||
int r = tr + rsr[d];
|
||||
while (on_board(f, r))
|
||||
{
|
||||
char p = b->squares[idx_from_fr(f, r)];
|
||||
if (p != '.')
|
||||
{
|
||||
if (by_white ? (p == 'R' || p == 'Q') : (p == 'r' || p == 'q'))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
f += rsf[d];
|
||||
r += rsr[d];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int count_attackers(const Board *b, int target_idx, int by_white) {
|
||||
int tf = file_of(target_idx), tr = rank_of(target_idx);
|
||||
int cnt = 0;
|
||||
const int kdf[8] = {1,2, 2,1, -1,-2, -2,-1};
|
||||
const int kdr[8] = {2,1, -1,-2, 2,1, -1,-2};
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
int f = tf + kdf[i], r = tr + kdr[i];
|
||||
if (on_board(f, r)) { char p = b->squares[idx_from_fr(f, r)]; if (by_white ? p == 'N' : p == 'n') cnt++; }
|
||||
}
|
||||
for (int df = -1; df <= 1; ++df) for (int dr = -1; dr <= 1; ++dr) if (df || dr) {
|
||||
int f = tf + df, r = tr + dr; if (on_board(f, r)) { char p = b->squares[idx_from_fr(f, r)]; if (by_white ? p == 'K' : p == 'k') cnt++; }
|
||||
}
|
||||
if (by_white) {
|
||||
int f1 = tf - 1, r1 = tr - 1; int f2 = tf + 1, r2 = tr - 1;
|
||||
if (on_board(f1, r1) && b->squares[idx_from_fr(f1, r1)] == 'P') cnt++;
|
||||
if (on_board(f2, r2) && b->squares[idx_from_fr(f2, r2)] == 'P') cnt++;
|
||||
} else {
|
||||
int f1 = tf - 1, r1 = tr + 1; int f2 = tf + 1, r2 = tr + 1;
|
||||
if (on_board(f1, r1) && b->squares[idx_from_fr(f1, r1)] == 'p') cnt++;
|
||||
if (on_board(f2, r2) && b->squares[idx_from_fr(f2, r2)] == 'p') cnt++;
|
||||
}
|
||||
const int dsf[4] = {1, 1, -1, -1};
|
||||
const int dsr[4] = {1, -1, 1, -1};
|
||||
for (int d = 0; d < 4; ++d) {
|
||||
int f = tf + dsf[d], r = tr + dsr[d];
|
||||
while (on_board(f, r)) { char p = b->squares[idx_from_fr(f, r)]; if (p != '.') { if (by_white ? (p == 'B' || p == 'Q') : (p == 'b' || p == 'q')) cnt++; break; } f += dsf[d]; r += dsr[d]; }
|
||||
}
|
||||
const int rsf[4] = {1, -1, 0, 0};
|
||||
const int rsr[4] = {0, 0, 1, -1};
|
||||
for (int d = 0; d < 4; ++d) {
|
||||
int f = tf + rsf[d], r = tr + rsr[d];
|
||||
while (on_board(f, r)) { char p = b->squares[idx_from_fr(f, r)]; if (p != '.') { if (by_white ? (p == 'R' || p == 'Q') : (p == 'r' || p == 'q')) cnt++; break; } f += rsf[d]; r += rsr[d]; }
|
||||
}
|
||||
return cnt;
|
||||
static int count_attackers(const Board *b, int target_idx, int by_white)
|
||||
{
|
||||
int tf = file_of(target_idx);
|
||||
int tr = rank_of(target_idx);
|
||||
int cnt = 0;
|
||||
const int kdf[8] = {1, 2, 2, 1, -1, -2, -2, -1};
|
||||
const int kdr[8] = {2, 1, -1, -2, 2, 1, -1, -2};
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
int f = tf + kdf[i];
|
||||
int r = tr + kdr[i];
|
||||
if (on_board(f, r))
|
||||
{
|
||||
char p = b->squares[idx_from_fr(f, r)];
|
||||
if (by_white ? p == 'N' : p == 'n')
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int df = -1; df <= 1; ++df)
|
||||
{
|
||||
for (int dr = -1; dr <= 1; ++dr)
|
||||
{
|
||||
if (df || dr)
|
||||
{
|
||||
int f = tf + df;
|
||||
int r = tr + dr;
|
||||
if (on_board(f, r))
|
||||
{
|
||||
char p = b->squares[idx_from_fr(f, r)];
|
||||
if (by_white ? p == 'K' : p == 'k')
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (by_white)
|
||||
{
|
||||
int f1 = tf - 1;
|
||||
int r1 = tr - 1;
|
||||
int f2 = tf + 1;
|
||||
int r2 = tr - 1;
|
||||
if (on_board(f1, r1) && b->squares[idx_from_fr(f1, r1)] == 'P')
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
if (on_board(f2, r2) && b->squares[idx_from_fr(f2, r2)] == 'P')
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int f1 = tf - 1;
|
||||
int r1 = tr + 1;
|
||||
int f2 = tf + 1;
|
||||
int r2 = tr + 1;
|
||||
if (on_board(f1, r1) && b->squares[idx_from_fr(f1, r1)] == 'p')
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
if (on_board(f2, r2) && b->squares[idx_from_fr(f2, r2)] == 'p')
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
const int dsf[4] = {1, 1, -1, -1};
|
||||
const int dsr[4] = {1, -1, 1, -1};
|
||||
for (int d = 0; d < 4; ++d)
|
||||
{
|
||||
int f = tf + dsf[d];
|
||||
int r = tr + dsr[d];
|
||||
while (on_board(f, r))
|
||||
{
|
||||
char p = b->squares[idx_from_fr(f, r)];
|
||||
if (p != '.')
|
||||
{
|
||||
if (by_white ? (p == 'B' || p == 'Q') : (p == 'b' || p == 'q'))
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
f += dsf[d];
|
||||
r += dsr[d];
|
||||
}
|
||||
}
|
||||
const int rsf[4] = {1, -1, 0, 0};
|
||||
const int rsr[4] = {0, 0, 1, -1};
|
||||
for (int d = 0; d < 4; ++d)
|
||||
{
|
||||
int f = tf + rsf[d];
|
||||
int r = tr + rsr[d];
|
||||
while (on_board(f, r))
|
||||
{
|
||||
char p = b->squares[idx_from_fr(f, r)];
|
||||
if (p != '.')
|
||||
{
|
||||
if (by_white ? (p == 'R' || p == 'Q') : (p == 'r' || p == 'q'))
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
f += rsf[d];
|
||||
r += rsr[d];
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static int material_cp(const Board *b) {
|
||||
int w = 0, bl = 0; for (int i = 0; i < 64; ++i) { char p = b->squares[i]; if (p == '.') continue; int v = piece_value_cp(p); if (is_white(p)) w += v; else bl += v; } return w - bl;
|
||||
static int material_cp(const Board *b)
|
||||
{
|
||||
int w = 0;
|
||||
int bl = 0;
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
char p = b->squares[i];
|
||||
if (p == '.')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int v = piece_value_cp(p);
|
||||
if (is_white(p))
|
||||
{
|
||||
w += v;
|
||||
}
|
||||
else
|
||||
{
|
||||
bl += v;
|
||||
}
|
||||
}
|
||||
return w - bl;
|
||||
}
|
||||
|
||||
static int parse_uci(const char *uci, int *from, int *to, char *prom) {
|
||||
if (!uci || strlen(uci) < 4) return 0;
|
||||
int f1 = uci[0] - 'a';
|
||||
int r1 = uci[1] - '1';
|
||||
int f2 = uci[2] - 'a';
|
||||
int r2 = uci[3] - '1';
|
||||
if (!on_board(f1, r1) || !on_board(f2, r2)) return 0;
|
||||
*from = idx_from_fr(f1, r1);
|
||||
*to = idx_from_fr(f2, r2);
|
||||
*prom = (uci[4] ? uci[4] : 0);
|
||||
return 1;
|
||||
static int parse_uci(const char *uci, int *from, int *to, char *prom)
|
||||
{
|
||||
if (!uci || strlen(uci) < 4)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int f1 = uci[0] - 'a';
|
||||
int r1 = uci[1] - '1';
|
||||
int f2 = uci[2] - 'a';
|
||||
int r2 = uci[3] - '1';
|
||||
if (!on_board(f1, r1) || !on_board(f2, r2))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
*from = idx_from_fr(f1, r1);
|
||||
*to = idx_from_fr(f2, r2);
|
||||
*prom = (uci[4] ? uci[4] : 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void apply_move(const Board *in, const char *uci, Board *out, int *cap_cp, int *prom_gain_cp) {
|
||||
*out = *in; *cap_cp = 0; *prom_gain_cp = 0; int from, to; char prom; if (!parse_uci(uci, &from, &to, &prom)) return; char mover = out->squares[from]; char captured = out->squares[to]; if (captured != '.') *cap_cp = piece_value_cp(captured); out->squares[to] = mover; out->squares[from] = '.'; if (prom) { int is_w = is_white(mover); char p = (char)tolower((unsigned char)prom); char prom_piece = p == 'q' ? (is_w ? 'Q' : 'q') : p == 'r' ? (is_w ? 'R' : 'r') : p == 'b' ? (is_w ? 'B' : 'b') : (is_w ? 'N' : 'n'); int gain = piece_value_cp(prom_piece) - piece_value_cp(is_w ? 'P' : 'p'); *prom_gain_cp = gain; out->squares[to] = prom_piece; } out->white_to_move = !in->white_to_move; }
|
||||
static void apply_move(const Board *in, const char *uci, Board *out, int *cap_cp, int *prom_gain_cp)
|
||||
{
|
||||
*out = *in;
|
||||
*cap_cp = 0;
|
||||
*prom_gain_cp = 0;
|
||||
int from;
|
||||
int to;
|
||||
char prom = 0;
|
||||
if (!parse_uci(uci, &from, &to, &prom))
|
||||
{
|
||||
return;
|
||||
}
|
||||
char mover = out->squares[from];
|
||||
char captured = out->squares[to];
|
||||
if (captured != '.')
|
||||
{
|
||||
*cap_cp = piece_value_cp(captured);
|
||||
}
|
||||
out->squares[to] = mover;
|
||||
out->squares[from] = '.';
|
||||
if (prom)
|
||||
{
|
||||
int is_w = is_white(mover);
|
||||
char p = (char)tolower((unsigned char)prom);
|
||||
char prom_piece = p == 'q' ? (is_w ? 'Q' : 'q')
|
||||
: p == 'r' ? (is_w ? 'R' : 'r')
|
||||
: p == 'b' ? (is_w ? 'B' : 'b')
|
||||
: (is_w ? 'N' : 'n');
|
||||
int gain = piece_value_cp(prom_piece) - piece_value_cp(is_w ? 'P' : 'p');
|
||||
*prom_gain_cp = gain;
|
||||
out->squares[to] = prom_piece;
|
||||
}
|
||||
out->white_to_move = !in->white_to_move;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char uci[16];
|
||||
double cap_cp;
|
||||
double prom_cp;
|
||||
double mat_cp;
|
||||
double atk_opp_king;
|
||||
double opp_king_mob;
|
||||
double piece_cp;
|
||||
double opp_min_att_cp;
|
||||
double us_min_att_cp;
|
||||
double see_cp;
|
||||
double risk_cp;
|
||||
int gives_check;
|
||||
double score;
|
||||
typedef struct
|
||||
{
|
||||
char uci[16];
|
||||
double cap_cp;
|
||||
double prom_cp;
|
||||
double mat_cp;
|
||||
double atk_opp_king;
|
||||
double opp_king_mob;
|
||||
double piece_cp;
|
||||
double opp_min_att_cp;
|
||||
double us_min_att_cp;
|
||||
double see_cp;
|
||||
double risk_cp;
|
||||
int gives_check;
|
||||
double score;
|
||||
} MoveInfo;
|
||||
|
||||
static double score_move(const MoveInfo *m, unsigned int seed) {
|
||||
double s = 0.0;
|
||||
if (m->gives_check) {
|
||||
double add = 200.0 + 40.0 * m->atk_opp_king - 35.0 * m->opp_king_mob;
|
||||
if (m->opp_king_mob <= 0.0) add += 800.0;
|
||||
s += add;
|
||||
}
|
||||
s += 1.5 * m->cap_cp;
|
||||
if (m->cap_cp > 0.0) {
|
||||
double exch = m->cap_cp - m->piece_cp;
|
||||
s += (m->piece_cp <= 120.0 ? 1.0 : 3.0) * exch;
|
||||
if (m->piece_cp >= 850.0) s -= 150.0;
|
||||
}
|
||||
s += 2.0 * m->prom_cp;
|
||||
s += 1.2 * m->mat_cp;
|
||||
s += 0.2 * m->see_cp;
|
||||
s -= 1.0 * m->risk_cp;
|
||||
double jitter = (double)(seed % 1000) / 1000000.0;
|
||||
return s + jitter;
|
||||
static double score_move(const MoveInfo *m, unsigned int seed)
|
||||
{
|
||||
double s = 0.0;
|
||||
if (m->gives_check)
|
||||
{
|
||||
double add = 200.0 + (40.0 * m->atk_opp_king) - (35.0 * m->opp_king_mob);
|
||||
if (m->opp_king_mob <= 0.0)
|
||||
{
|
||||
add += 800.0;
|
||||
}
|
||||
s += add;
|
||||
}
|
||||
s += 1.5 * m->cap_cp;
|
||||
if (m->cap_cp > 0.0)
|
||||
{
|
||||
double exch = m->cap_cp - m->piece_cp;
|
||||
s += (m->piece_cp <= 120.0 ? 1.0 : 3.0) * exch;
|
||||
if (m->piece_cp >= 850.0)
|
||||
{
|
||||
s -= 150.0;
|
||||
}
|
||||
}
|
||||
s += 2.0 * m->prom_cp;
|
||||
s += 1.2 * m->mat_cp;
|
||||
s += 0.2 * m->see_cp;
|
||||
s -= 1.0 * m->risk_cp;
|
||||
double jitter = (double)(seed % 1000) / 1000000.0;
|
||||
return s + jitter;
|
||||
}
|
||||
|
||||
static unsigned int parse_seed(int *pargc, char ***pargv) {
|
||||
unsigned int seed = (unsigned int)time(NULL) ^ (unsigned int)getpid();
|
||||
int argc = *pargc; char **argv = *pargv;
|
||||
for (int i = 1; i < argc; ++i) if (strcmp(argv[i], "--seed") == 0 && i + 1 < argc) {
|
||||
seed = (unsigned int)strtoul(argv[i + 1], NULL, 10);
|
||||
for (int j = i; j + 2 < argc; ++j) argv[j] = argv[j + 2];
|
||||
*pargc -= 2; return seed;
|
||||
}
|
||||
return seed;
|
||||
static unsigned int parse_seed(int *pargc, char ***pargv)
|
||||
{
|
||||
unsigned int seed = (unsigned int)time(NULL) ^ (unsigned int)getpid();
|
||||
int argc = *pargc;
|
||||
char **argv = *pargv;
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
if (strcmp(argv[i], "--seed") == 0 && i + 1 < argc)
|
||||
{
|
||||
seed = (unsigned int)strtoul(argv[i + 1], NULL, 10);
|
||||
for (int j = i; j + 2 < argc; ++j)
|
||||
{
|
||||
argv[j] = argv[j + 2];
|
||||
}
|
||||
*pargc -= 2;
|
||||
return seed;
|
||||
}
|
||||
}
|
||||
return seed;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc <= 1) { fprintf(stderr, "usage: %s [--seed N] [--fen FEN] [--explain] [--analyze UCI] <moves...>\n", argv[0]); return 1; }
|
||||
unsigned int seed = parse_seed(&argc, &argv); srand(seed);
|
||||
int explain = 0; const char *analyze_uci = NULL; const char *fen = NULL;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (strcmp(argv[i], "--explain") == 0) { explain = 1; for (int j = i; j + 1 < argc; ++j) argv[j] = argv[j + 1]; argc -= 1; i -= 1; }
|
||||
else if (strcmp(argv[i], "--fen") == 0 && i + 1 < argc) { fen = argv[i + 1]; for (int j = i; j + 2 < argc; ++j) argv[j] = argv[j + 2]; argc -= 2; i -= 1; }
|
||||
else if (strcmp(argv[i], "--analyze") == 0 && i + 1 < argc) { analyze_uci = argv[i + 1]; for (int j = i; j + 2 < argc; ++j) argv[j] = argv[j + 2]; argc -= 2; i -= 1; }
|
||||
}
|
||||
if (argc <= 1) { fprintf(stderr, "no moves provided\n"); return 1; }
|
||||
int n = argc - 1; char **moves = &argv[1];
|
||||
Board board; int have_pos = 0; if (fen) { if (!parse_fen(&board, fen)) { fprintf(stderr, "invalid FEN\n"); return 1; } have_pos = 1; }
|
||||
int base_mat = 0; if (have_pos) base_mat = material_cp(&board);
|
||||
MoveInfo *arr = (MoveInfo*)calloc((size_t)n, sizeof(MoveInfo)); if (!arr) { fprintf(stderr, "alloc failed\n"); return 1; }
|
||||
for (int i = 0; i < n; ++i) {
|
||||
strncpy(arr[i].uci, moves[i], sizeof(arr[i].uci) - 1); arr[i].uci[sizeof(arr[i].uci) - 1] = '\0';
|
||||
if (!have_pos) { arr[i].score = (double)rand() / (double)RAND_MAX; continue; }
|
||||
Board after = board; int cap = 0, pg = 0; apply_move(&board, arr[i].uci, &after, &cap, &pg);
|
||||
int mat_after = material_cp(&after); int mat_raw = mat_after - base_mat; int mat_signed = board.white_to_move ? mat_raw : -mat_raw;
|
||||
arr[i].cap_cp = cap; arr[i].prom_cp = pg; arr[i].mat_cp = (double)mat_signed;
|
||||
int from, to; char pr;
|
||||
if (parse_uci(arr[i].uci, &from, &to, &pr)) {
|
||||
char landed = after.squares[to]; arr[i].piece_cp = piece_value_cp(landed);
|
||||
int opp_is_white = after.white_to_move; int us_is_white = !after.white_to_move;
|
||||
int opp_min = 0, us_min = 0;
|
||||
// Use min of attackers by value (crude)
|
||||
// We'll reuse piece values by scanning all pieces; simpler via count_attackers() surrogate
|
||||
// Here we approximate with: if square attacked at all, assume min attacker is pawn (100)
|
||||
if (sq_attacked_by(&after, to, opp_is_white)) opp_min = 100;
|
||||
if (sq_attacked_by(&after, to, us_is_white)) us_min = 100;
|
||||
arr[i].opp_min_att_cp = opp_min; arr[i].us_min_att_cp = us_min;
|
||||
arr[i].see_cp = (cap > 0) ? ((double)cap - (double)opp_min) : 0.0;
|
||||
if (cap == 0 && opp_min > 0 && us_min == 0) { double risk = (double)opp_min; if (risk > arr[i].piece_cp) risk = arr[i].piece_cp; arr[i].risk_cp = risk; }
|
||||
}
|
||||
int opp_white = !board.white_to_move; int opp_king = find_king(&after, opp_white);
|
||||
int gives = 0; if (opp_king >= 0) { gives = sq_attacked_by(&after, opp_king, !after.white_to_move); int atk = count_attackers(&after, opp_king, !after.white_to_move); int mob = 0; int kf = file_of(opp_king), kr = rank_of(opp_king); for (int df=-1; df<=1; ++df) for (int dr=-1; dr<=1; ++dr) if (df||dr) { int f=kf+df, r=kr+dr; if (!on_board(f,r)) continue; int idx=idx_from_fr(f,r); char occ=after.squares[idx]; if (occ!='.' && (opp_white ? is_white(occ) : is_black(occ))) continue; if (!sq_attacked_by(&after, idx, !after.white_to_move)) mob++; } arr[i].atk_opp_king = atk; arr[i].opp_king_mob = mob; }
|
||||
arr[i].gives_check = gives;
|
||||
unsigned int local = seed ^ (unsigned int)i * 2654435761u; arr[i].score = score_move(&arr[i], local);
|
||||
}
|
||||
double best_score = -1e300; int best_idx = -1; for (int i = 0; i < n; ++i) if (arr[i].score > best_score) { best_score = arr[i].score; best_idx = i; }
|
||||
if (best_idx < 0) { free(arr); fprintf(stderr, "no moves\n"); return 1; }
|
||||
if (!explain) { printf("%s\n", arr[best_idx].uci); free(arr); return 0; }
|
||||
printf("{\n");
|
||||
printf(" \"seed\": %u,\n", seed);
|
||||
if (have_pos) { printf(" \"fen\": \"%s\",\n", fen); printf(" \"side_to_move\": \"%s\",\n", board.white_to_move ? "white" : "black"); printf(" \"base_material_cp\": %d,\n", base_mat); }
|
||||
printf(" \"n\": %d,\n", n);
|
||||
printf(" \"moves\": ["); for (int i = 0; i < n; ++i) { printf("\"%s\"%s", arr[i].uci, (i+1<n?", ":"")); } printf("],\n");
|
||||
printf(" \"scores\": ["); for (int i = 0; i < n; ++i) { printf("%.6f%s", arr[i].score, (i+1<n?", ":"")); } printf("],\n");
|
||||
printf(" \"chosen_index\": %d,\n", best_idx);
|
||||
printf(" \"chosen_move\": \"%s\"", arr[best_idx].uci);
|
||||
if (analyze_uci) {
|
||||
int cand_idx = -1; for (int i = 0; i < n; ++i) if (strcmp(arr[i].uci, analyze_uci) == 0) { cand_idx = i; break; }
|
||||
double cand_score = (cand_idx >= 0 ? arr[cand_idx].score : -1.0);
|
||||
const char *cmp = "unknown"; if (cand_idx >= 0) cmp = (cand_score > best_score ? "higher" : (cand_score < best_score ? "lower" : "equal"));
|
||||
printf(",\n \"analyze\": { \"candidate\": \"%s\", \"candidate_index\": %d, \"candidate_score\": %.6f, \"compare_to_chosen\": \"%s\" }\n", analyze_uci, cand_idx, cand_score, cmp);
|
||||
} else {
|
||||
printf("\n");
|
||||
}
|
||||
printf("}\n");
|
||||
free(arr);
|
||||
return 0;
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc <= 1)
|
||||
{
|
||||
fprintf(stderr, "usage: %s [--seed N] [--fen FEN] [--explain] [--analyze UCI] <moves...>\n",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
unsigned int seed = parse_seed(&argc, &argv);
|
||||
srand(seed);
|
||||
int explain = 0;
|
||||
const char *analyze_uci = NULL;
|
||||
const char *fen = NULL;
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
if (strcmp(argv[i], "--explain") == 0)
|
||||
{
|
||||
explain = 1;
|
||||
for (int j = i; j + 1 < argc; ++j)
|
||||
{
|
||||
argv[j] = argv[j + 1];
|
||||
}
|
||||
argc -= 1;
|
||||
i -= 1;
|
||||
}
|
||||
else if (strcmp(argv[i], "--fen") == 0 && i + 1 < argc)
|
||||
{
|
||||
fen = argv[i + 1];
|
||||
for (int j = i; j + 2 < argc; ++j)
|
||||
{
|
||||
argv[j] = argv[j + 2];
|
||||
}
|
||||
argc -= 2;
|
||||
i -= 1;
|
||||
}
|
||||
else if (strcmp(argv[i], "--analyze") == 0 && i + 1 < argc)
|
||||
{
|
||||
analyze_uci = argv[i + 1];
|
||||
for (int j = i; j + 2 < argc; ++j)
|
||||
{
|
||||
argv[j] = argv[j + 2];
|
||||
}
|
||||
argc -= 2;
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
if (argc <= 1)
|
||||
{
|
||||
fprintf(stderr, "no moves provided\n");
|
||||
return 1;
|
||||
}
|
||||
int n = argc - 1;
|
||||
char **moves = &argv[1];
|
||||
Board board;
|
||||
int have_pos = 0;
|
||||
if (fen)
|
||||
{
|
||||
if (!parse_fen(&board, fen))
|
||||
{
|
||||
fprintf(stderr, "invalid FEN\n");
|
||||
return 1;
|
||||
}
|
||||
have_pos = 1;
|
||||
}
|
||||
int base_mat = 0;
|
||||
if (have_pos)
|
||||
{
|
||||
base_mat = material_cp(&board);
|
||||
}
|
||||
MoveInfo *arr = (MoveInfo *)calloc((size_t)n, sizeof(MoveInfo));
|
||||
if (!arr)
|
||||
{
|
||||
fprintf(stderr, "alloc failed\n");
|
||||
return 1;
|
||||
}
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
strncpy(arr[i].uci, moves[i], sizeof(arr[i].uci) - 1);
|
||||
arr[i].uci[sizeof(arr[i].uci) - 1] = '\0';
|
||||
if (!have_pos)
|
||||
{
|
||||
arr[i].score = (double)rand() / (double)RAND_MAX;
|
||||
continue;
|
||||
}
|
||||
Board after = board;
|
||||
int cap = 0;
|
||||
int pg = 0;
|
||||
apply_move(&board, arr[i].uci, &after, &cap, &pg);
|
||||
int mat_after = material_cp(&after);
|
||||
int mat_raw = mat_after - base_mat;
|
||||
int mat_signed = board.white_to_move ? mat_raw : -mat_raw;
|
||||
arr[i].cap_cp = cap;
|
||||
arr[i].prom_cp = pg;
|
||||
arr[i].mat_cp = (double)mat_signed;
|
||||
int from;
|
||||
int to;
|
||||
char pr = 0;
|
||||
if (parse_uci(arr[i].uci, &from, &to, &pr))
|
||||
{
|
||||
char landed = after.squares[to];
|
||||
arr[i].piece_cp = piece_value_cp(landed);
|
||||
int opp_is_white = after.white_to_move;
|
||||
int us_is_white = !after.white_to_move;
|
||||
int opp_min = 0;
|
||||
int us_min = 0;
|
||||
// Use min of attackers by value (crude)
|
||||
// We'll reuse piece values by scanning all pieces; simpler via count_attackers()
|
||||
// surrogate Here we approximate with: if square attacked at all, assume min attacker is
|
||||
// pawn (100)
|
||||
if (sq_attacked_by(&after, to, opp_is_white))
|
||||
{
|
||||
opp_min = 100;
|
||||
}
|
||||
if (sq_attacked_by(&after, to, us_is_white))
|
||||
{
|
||||
us_min = 100;
|
||||
}
|
||||
arr[i].opp_min_att_cp = opp_min;
|
||||
arr[i].us_min_att_cp = us_min;
|
||||
arr[i].see_cp = (cap > 0) ? ((double)cap - (double)opp_min) : 0.0;
|
||||
if (cap == 0 && opp_min > 0 && us_min == 0)
|
||||
{
|
||||
double risk = (double)opp_min;
|
||||
if (risk > arr[i].piece_cp)
|
||||
{
|
||||
risk = arr[i].piece_cp;
|
||||
}
|
||||
arr[i].risk_cp = risk;
|
||||
}
|
||||
}
|
||||
int opp_white = !board.white_to_move;
|
||||
int opp_king = find_king(&after, opp_white);
|
||||
int gives = 0;
|
||||
if (opp_king >= 0)
|
||||
{
|
||||
gives = sq_attacked_by(&after, opp_king, !after.white_to_move);
|
||||
int atk = count_attackers(&after, opp_king, !after.white_to_move);
|
||||
int mob = 0;
|
||||
int kf = file_of(opp_king);
|
||||
int kr = rank_of(opp_king);
|
||||
for (int df = -1; df <= 1; ++df)
|
||||
{
|
||||
for (int dr = -1; dr <= 1; ++dr)
|
||||
{
|
||||
if (df || dr)
|
||||
{
|
||||
int f = kf + df;
|
||||
int r = kr + dr;
|
||||
if (!on_board(f, r))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int idx = idx_from_fr(f, r);
|
||||
char occ = after.squares[idx];
|
||||
if (occ != '.' && (opp_white ? is_white(occ) : is_black(occ)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!sq_attacked_by(&after, idx, !after.white_to_move))
|
||||
{
|
||||
mob++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
arr[i].atk_opp_king = atk;
|
||||
arr[i].opp_king_mob = mob;
|
||||
}
|
||||
arr[i].gives_check = gives;
|
||||
unsigned int local = seed ^ ((unsigned int)i * 2654435761U);
|
||||
arr[i].score = score_move(&arr[i], local);
|
||||
}
|
||||
double best_score = -1e300;
|
||||
int best_idx = -1;
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
if (arr[i].score > best_score)
|
||||
{
|
||||
best_score = arr[i].score;
|
||||
best_idx = i;
|
||||
}
|
||||
}
|
||||
if (best_idx < 0)
|
||||
{
|
||||
free(arr);
|
||||
fprintf(stderr, "no moves\n");
|
||||
return 1;
|
||||
}
|
||||
if (!explain)
|
||||
{
|
||||
printf("%s\n", arr[best_idx].uci);
|
||||
free(arr);
|
||||
return 0;
|
||||
}
|
||||
printf("{\n");
|
||||
printf(" \"seed\": %u,\n", seed);
|
||||
if (have_pos)
|
||||
{
|
||||
printf(" \"fen\": \"%s\",\n", fen);
|
||||
printf(" \"side_to_move\": \"%s\",\n", board.white_to_move ? "white" : "black");
|
||||
printf(" \"base_material_cp\": %d,\n", base_mat);
|
||||
}
|
||||
printf(" \"n\": %d,\n", n);
|
||||
printf(" \"moves\": [");
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
printf("\"%s\"%s", arr[i].uci, (i + 1 < n ? ", " : ""));
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"scores\": [");
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
printf("%.6f%s", arr[i].score, (i + 1 < n ? ", " : ""));
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"chosen_index\": %d,\n", best_idx);
|
||||
printf(" \"chosen_move\": \"%s\"", arr[best_idx].uci);
|
||||
if (analyze_uci)
|
||||
{
|
||||
int cand_idx = -1;
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
if (strcmp(arr[i].uci, analyze_uci) == 0)
|
||||
{
|
||||
cand_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
double cand_score = (cand_idx >= 0 ? arr[cand_idx].score : -1.0);
|
||||
const char *cmp = "unknown";
|
||||
if (cand_idx >= 0)
|
||||
{
|
||||
cmp = (cand_score > best_score ? "higher"
|
||||
: (cand_score < best_score ? "lower" : "equal"));
|
||||
}
|
||||
printf(",\n \"analyze\": { \"candidate\": \"%s\", \"candidate_index\": %d, "
|
||||
"\"candidate_score\": %.6f, \"compare_to_chosen\": \"%s\" }\n",
|
||||
analyze_uci, cand_idx, cand_score, cmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("\n");
|
||||
}
|
||||
printf("}\n");
|
||||
free(arr);
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -4,46 +4,68 @@
|
||||
#include <stdint.h>
|
||||
|
||||
// 0x88 board representation
|
||||
#define BOARD_SIZE 128
|
||||
enum
|
||||
{
|
||||
BOARD_SIZE = 128
|
||||
};
|
||||
|
||||
typedef enum { WHITE = 0, BLACK = 1 } Color;
|
||||
typedef enum
|
||||
{
|
||||
WHITE = 0,
|
||||
BLACK = 1
|
||||
} Color;
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
EMPTY = 0,
|
||||
WP = 1, WN, WB, WR, WQ, WK,
|
||||
BP = 7, BN, BB, BR, BQ, BK
|
||||
WP = 1,
|
||||
WN = 2,
|
||||
WB = 3,
|
||||
WR = 4,
|
||||
WQ = 5,
|
||||
WK = 6,
|
||||
BP = 7,
|
||||
BN = 8,
|
||||
BB = 9,
|
||||
BR = 10,
|
||||
BQ = 11,
|
||||
BK = 12
|
||||
} Piece;
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
// from and to squares in 0x88 (0..127), promotion piece in Piece enum or 0
|
||||
uint8_t from, to;
|
||||
uint8_t promo; // 0 if none
|
||||
uint8_t is_capture; // 1 if capture
|
||||
uint8_t promo; // 0 if none
|
||||
uint8_t is_capture; // 1 if capture
|
||||
uint8_t is_enpassant; // 1 if en-passant capture
|
||||
uint8_t is_castle; // 1 if castle
|
||||
uint8_t is_castle; // 1 if castle
|
||||
} Move;
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
Piece board[BOARD_SIZE];
|
||||
Color side;
|
||||
// Castling rights: bit 0 white king-side, 1 white queen-side, 2 black king-side, 3 black queen-side
|
||||
// Castling rights: bit 0 white king-side, 1 white queen-side, 2 black king-side, 3 black
|
||||
// queen-side
|
||||
uint8_t castle;
|
||||
int8_t ep_square; // -1 if none, else 0x88 square index
|
||||
int halfmove_clock;
|
||||
int fullmove_number;
|
||||
int8_t ep_square; // -1 if none, else 0x88 square index
|
||||
int halfmove_clock;
|
||||
int fullmove_number;
|
||||
} Position;
|
||||
|
||||
// Parsing and utilities
|
||||
int parse_fen(Position *pos, const char *fen);
|
||||
int parse_fen(Position *pos, const char *fen);
|
||||
void set_startpos(Position *pos);
|
||||
int square_from_algebraic(const char *uci4, int is_from);
|
||||
int move_from_uci(const Position *pos, const char *uci, Move *out);
|
||||
int square_from_algebraic(const char *uci4, int is_from);
|
||||
int move_from_uci(const Position *pos, const char *uci, Move *out);
|
||||
void make_move(Position *pos, const Move *m, Piece *captured_out);
|
||||
void unmake_move(Position *pos, const Move *m, Piece captured);
|
||||
int in_check(const Position *pos, Color side);
|
||||
int in_check(const Position *pos, Color side);
|
||||
|
||||
// Move generation
|
||||
// Generates all pseudo-legal moves into moves[], returns count. If captures_only!=0, only captures (incl. ep) are generated
|
||||
// Generates all pseudo-legal moves into moves[], returns count. If captures_only!=0, only captures
|
||||
// (incl. ep) are generated
|
||||
int gen_moves(const Position *pos, Move *moves, int max_moves, int captures_only);
|
||||
int gen_moves_pseudo(const Position *pos, Move *moves, int max_moves, int captures_only);
|
||||
|
||||
|
||||
@ -1,66 +1,140 @@
|
||||
#include "movegen.h"
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static unsigned long long perft(Position pos, int depth){
|
||||
if (depth==0) return 1ULL;
|
||||
Move moves[256];
|
||||
static unsigned long long perft(Position pos, int depth)
|
||||
{
|
||||
if (depth == 0)
|
||||
{
|
||||
return 1ULL;
|
||||
}
|
||||
Move moves[256];
|
||||
unsigned long long nodes = 0ULL;
|
||||
int n = gen_moves(&pos, moves, 256, 0);
|
||||
for (int i=0;i<n;i++){
|
||||
int n = gen_moves(&pos, moves, 256, 0);
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
Position child = pos;
|
||||
Piece cap=EMPTY;
|
||||
Piece cap = EMPTY;
|
||||
make_move(&child, &moves[i], &cap);
|
||||
nodes += perft(child, depth-1);
|
||||
nodes += perft(child, depth - 1);
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
static void uci_from_move(const Move *m, char *buf){
|
||||
int ff = (m->from & 7), fr = (m->from >> 4);
|
||||
int tf = (m->to & 7), tr = (m->to >> 4);
|
||||
buf[0] = 'a' + ff; buf[1] = '1' + fr; buf[2] = 'a' + tf; buf[3] = '1' + tr; int i=4;
|
||||
if (m->promo){ char pc='q'; switch(m->promo){ case WQ: case BQ: pc='q'; break; case WR: case BR: pc='r'; break; case WB: case BB: pc='b'; break; case WN: case BN: pc='n'; break; default: pc='q'; }
|
||||
buf[i++]=pc; }
|
||||
buf[i]=0;
|
||||
static void uci_from_move(const Move *m, char *buf)
|
||||
{
|
||||
int ff = (m->from & 7);
|
||||
int fr = (m->from >> 4);
|
||||
int tf = (m->to & 7);
|
||||
int tr = (m->to >> 4);
|
||||
buf[0] = (char)('a' + ff);
|
||||
buf[1] = (char)('1' + fr);
|
||||
buf[2] = (char)('a' + tf);
|
||||
buf[3] = (char)('1' + tr);
|
||||
int i = 4;
|
||||
if (m->promo)
|
||||
{
|
||||
char pc = 'q';
|
||||
switch (m->promo)
|
||||
{
|
||||
case WQ:
|
||||
case BQ:
|
||||
pc = 'q';
|
||||
break;
|
||||
case WR:
|
||||
case BR:
|
||||
pc = 'r';
|
||||
break;
|
||||
case WB:
|
||||
case BB:
|
||||
pc = 'b';
|
||||
break;
|
||||
case WN:
|
||||
case BN:
|
||||
pc = 'n';
|
||||
break;
|
||||
default:
|
||||
pc = 'q';
|
||||
}
|
||||
buf[i++] = pc;
|
||||
}
|
||||
buf[i] = 0;
|
||||
}
|
||||
|
||||
static void run_case(const char *fen, int depth, unsigned long long expected){
|
||||
Position p; if (!parse_fen(&p, fen)){ fprintf(stderr, "Bad FEN: %s\n", fen); return; }
|
||||
static void run_case(const char *fen, int depth, unsigned long long expected)
|
||||
{
|
||||
Position p;
|
||||
if (!parse_fen(&p, fen))
|
||||
{
|
||||
(void)fprintf(stderr, "Bad FEN: %s\n", fen);
|
||||
return;
|
||||
}
|
||||
unsigned long long n = perft(p, depth);
|
||||
printf("perft(%d) = %llu %s\n", depth, n, (expected? (n==expected?"OK":"MISMATCH"):""));
|
||||
printf("perft(%d) = %llu %s\n", depth, n,
|
||||
(expected ? (n == expected ? "OK" : "MISMATCH") : ""));
|
||||
}
|
||||
|
||||
int main(int argc, char**argv){
|
||||
if (argc>=3){
|
||||
const char *fen = argv[1];
|
||||
int depth = atoi(argv[2]);
|
||||
Position p; if (!parse_fen(&p, fen)){ fprintf(stderr, "Bad FEN input\n"); return 2; }
|
||||
if (argc>=4 && strcmp(argv[3], "--divide")==0){
|
||||
Move moves[256]; int n = gen_moves(&p, moves, 256, 0);
|
||||
unsigned long long total=0ULL;
|
||||
for (int i=0;i<n;i++){
|
||||
Position c = p; Piece cap=EMPTY; make_move(&c, &moves[i], &cap);
|
||||
unsigned long long sub = perft(c, depth-1);
|
||||
char u[8]; uci_from_move(&moves[i], u);
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc >= 3)
|
||||
{
|
||||
const char *fen = argv[1];
|
||||
char *depth_end = NULL;
|
||||
long depth_long = strtol(argv[2], &depth_end, 10);
|
||||
if (depth_end == argv[2] || *depth_end != '\0' || depth_long < 0 || depth_long > INT_MAX)
|
||||
{
|
||||
(void)fprintf(stderr, "Invalid depth value: %s\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
int depth = (int)depth_long;
|
||||
Position p;
|
||||
if (!parse_fen(&p, fen))
|
||||
{
|
||||
(void)fprintf(stderr, "Bad FEN input\n");
|
||||
return 2;
|
||||
}
|
||||
if (argc >= 4 && strcmp(argv[3], "--divide") == 0)
|
||||
{
|
||||
Move moves[256];
|
||||
int n = gen_moves(&p, moves, 256, 0);
|
||||
unsigned long long total = 0ULL;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
Position c = p;
|
||||
Piece cap = EMPTY;
|
||||
make_move(&c, &moves[i], &cap);
|
||||
unsigned long long sub = perft(c, depth - 1);
|
||||
char u[8];
|
||||
uci_from_move(&moves[i], u);
|
||||
printf("%s: %llu\n", u, sub);
|
||||
total += sub;
|
||||
}
|
||||
printf("Total: %llu\n", total);
|
||||
} else if (argc>=4 && strcmp(argv[3], "--divide-pseudo")==0){
|
||||
Move moves[256]; int n = gen_moves_pseudo(&p, moves, 256, 0);
|
||||
for (int i=0;i<n;i++){ char u[8]; uci_from_move(&moves[i], u); printf("%s\n", u); }
|
||||
}
|
||||
else if (argc >= 4 && strcmp(argv[3], "--divide-pseudo") == 0)
|
||||
{
|
||||
Move moves[256];
|
||||
int n = gen_moves_pseudo(&p, moves, 256, 0);
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
char u[8];
|
||||
uci_from_move(&moves[i], u);
|
||||
printf("%s\n", u);
|
||||
}
|
||||
printf("Total pseudo: %d\n", n);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned long long n = perft(p, depth);
|
||||
printf("%llu\n", n);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Some well-known positions (depth limited to be fast). Expected nodes are standard perft values.
|
||||
// Start position
|
||||
// Some well-known positions (depth limited to be fast). Expected nodes are standard perft
|
||||
// values. Start position
|
||||
run_case("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", 1, 20ULL);
|
||||
run_case("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", 2, 400ULL);
|
||||
// Kiwipete
|
||||
|
||||
@ -1,60 +1,112 @@
|
||||
#include "search.h"
|
||||
#include "movegen.h"
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
static int piece_value(Piece p){
|
||||
switch(p){
|
||||
case WP: case BP: return 100;
|
||||
case WN: case BN: return 320;
|
||||
case WB: case BB: return 330;
|
||||
case WR: case BR: return 500;
|
||||
case WQ: case BQ: return 900;
|
||||
case WK: case BK: return 0; // king is invaluable; PST handled later if needed
|
||||
default: return 0;
|
||||
static int piece_value(Piece p)
|
||||
{
|
||||
if (p == WK || p == BK)
|
||||
{
|
||||
return 0; // king is invaluable; PST handled later if needed
|
||||
}
|
||||
|
||||
switch (p)
|
||||
{
|
||||
case WP:
|
||||
case BP:
|
||||
return 100;
|
||||
case WN:
|
||||
case BN:
|
||||
return 320;
|
||||
case WB:
|
||||
case BB:
|
||||
return 330;
|
||||
case WR:
|
||||
case BR:
|
||||
return 500;
|
||||
case WQ:
|
||||
case BQ:
|
||||
return 900;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int evaluate(const Position *pos){
|
||||
int evaluate(const Position *pos)
|
||||
{
|
||||
int score = 0;
|
||||
for (int sq=0; sq<BOARD_SIZE; ++sq){
|
||||
if ((sq & 0x88)) { sq = (sq|7); continue; }
|
||||
for (int sq = 0; sq < BOARD_SIZE; ++sq)
|
||||
{
|
||||
if ((sq & 0x88))
|
||||
{
|
||||
sq = (sq | 7);
|
||||
continue;
|
||||
}
|
||||
Piece p = pos->board[sq];
|
||||
if (p==EMPTY) continue;
|
||||
if (p == EMPTY)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int v = piece_value(p);
|
||||
if (p>=WP && p<=WK) score += v; else if (p>=BP && p<=BK) score -= v;
|
||||
if (p >= WP && p <= WK)
|
||||
{
|
||||
score += v;
|
||||
}
|
||||
else if (p >= BP && p <= BK)
|
||||
{
|
||||
score -= v;
|
||||
}
|
||||
}
|
||||
// Score from side-to-move perspective
|
||||
return (pos->side==WHITE)? score : -score;
|
||||
return (pos->side == WHITE) ? score : -score;
|
||||
}
|
||||
|
||||
int alphabeta(Position pos, int depth, int alpha, int beta, int *pv_from, int *pv_to){
|
||||
if (depth<=0){
|
||||
int alphabeta(Position pos, int depth, int alpha, int beta, PrincipalVariation *pv)
|
||||
{
|
||||
if (depth <= 0)
|
||||
{
|
||||
return evaluate(&pos);
|
||||
}
|
||||
Move moves[256];
|
||||
int n = gen_moves(&pos, moves, 256, 0);
|
||||
if (n==0){
|
||||
int n = gen_moves(&pos, moves, 256, 0);
|
||||
if (n == 0)
|
||||
{
|
||||
// Checkmate or stalemate
|
||||
if (in_check(&pos, pos.side)) return -30000 + (10 - depth); // checkmated
|
||||
if (in_check(&pos, pos.side))
|
||||
{
|
||||
return -30000 + (10 - depth); // checkmated
|
||||
}
|
||||
return 0; // stalemate
|
||||
}
|
||||
|
||||
int best_score = INT_MIN/2;
|
||||
int best_from = -1, best_to = -1;
|
||||
for (int i=0;i<n;i++){
|
||||
int best_score = INT_MIN / 2;
|
||||
int best_from = -1;
|
||||
int best_to = -1;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
Position child = pos;
|
||||
Piece cap=EMPTY;
|
||||
Piece cap = EMPTY;
|
||||
make_move(&child, &moves[i], &cap);
|
||||
int score = -alphabeta(child, depth-1, -beta, -alpha, NULL, NULL);
|
||||
if (score > best_score){
|
||||
int score = -alphabeta(child, depth - 1, -beta, -alpha, NULL);
|
||||
if (score > best_score)
|
||||
{
|
||||
best_score = score;
|
||||
best_from = moves[i].from;
|
||||
best_to = moves[i].to;
|
||||
best_from = moves[i].from;
|
||||
best_to = moves[i].to;
|
||||
}
|
||||
if (best_score > alpha)
|
||||
{
|
||||
alpha = best_score;
|
||||
}
|
||||
if (alpha >= beta)
|
||||
{
|
||||
break; // beta cutoff
|
||||
}
|
||||
if (best_score > alpha) alpha = best_score;
|
||||
if (alpha >= beta) break; // beta cutoff
|
||||
}
|
||||
if (pv_from) *pv_from = best_from;
|
||||
if (pv_to) *pv_to = best_to;
|
||||
if (pv != NULL)
|
||||
{
|
||||
pv->from = best_from;
|
||||
pv->to = best_to;
|
||||
}
|
||||
return best_score;
|
||||
}
|
||||
|
||||
@ -3,15 +3,22 @@
|
||||
|
||||
#include "movegen.h"
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
int depth;
|
||||
int nodes;
|
||||
} SearchLimits;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int from;
|
||||
int to;
|
||||
} PrincipalVariation;
|
||||
|
||||
// Evaluate position in centipawns from the side-to-move perspective.
|
||||
int evaluate(const Position *pos);
|
||||
|
||||
// Negamax alpha-beta returning score in centipawns from side-to-move perspective.
|
||||
int alphabeta(Position pos, int depth, int alpha, int beta, int *pv_from, int *pv_to);
|
||||
int alphabeta(Position pos, int depth, int alpha, int beta, PrincipalVariation *pv);
|
||||
|
||||
#endif // SEARCH_H
|
||||
@ -22,6 +22,7 @@ err() { echo -e "${RED}✗${NC} $*"; }
|
||||
|
||||
ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
C_DIR="${ROOT_DIR}/C"
|
||||
AUTOFIX=${LINT_AUTOFIX:-1}
|
||||
|
||||
if [[ ! -d "${C_DIR}" ]]; then
|
||||
err "C directory not found at ${C_DIR}"
|
||||
@ -30,6 +31,8 @@ fi
|
||||
|
||||
ISSUES=0
|
||||
MISSING=()
|
||||
C_FILES=()
|
||||
C_SOURCES=()
|
||||
|
||||
need_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || MISSING+=("$1")
|
||||
@ -139,6 +142,75 @@ collect_files() {
|
||||
else
|
||||
ok "Found ${#C_FILES[@]} C-related files to check"
|
||||
fi
|
||||
mapfile -t C_SOURCES < <(find "${C_DIR}" -type f -name '*.c' \
|
||||
-not -path '*/.*' -not -path '*/build/*' -not -path '*/dist/*' -not -path '*/out/*' \
|
||||
-not -path '*/bin/*' -not -path '*/obj/*')
|
||||
}
|
||||
|
||||
apply_clang_format_fix() {
|
||||
if ! command -v clang-format >/dev/null 2>&1; then
|
||||
warn "clang-format unavailable; skipping auto-format"
|
||||
return
|
||||
fi
|
||||
if [[ ${#C_FILES[@]} -eq 0 ]]; then
|
||||
return
|
||||
fi
|
||||
info "Applying clang-format -i to source files"
|
||||
local formatted=0
|
||||
for f in "${C_FILES[@]}"; do
|
||||
if clang-format -i "$f" 2>/dev/null; then
|
||||
formatted=$((formatted+1))
|
||||
fi
|
||||
done
|
||||
ok "clang-format applied to ${formatted} file(s)"
|
||||
}
|
||||
|
||||
apply_clang_tidy_fix() {
|
||||
if ! command -v clang-tidy >/dev/null 2>&1; then
|
||||
warn "clang-tidy unavailable; skipping auto-fix"
|
||||
return
|
||||
fi
|
||||
if [[ ${#C_SOURCES[@]} -eq 0 ]]; then
|
||||
return
|
||||
fi
|
||||
local db="${C_DIR}/compile_commands.json"
|
||||
local used_db="no"
|
||||
if [[ -f "$db" ]] && head -n 1 "$db" | grep -q '\['; then
|
||||
used_db="yes"
|
||||
fi
|
||||
info "Applying clang-tidy --fix to C sources"
|
||||
local failures=0
|
||||
for f in "${C_SOURCES[@]}"; do
|
||||
local rel
|
||||
rel=$(realpath --relative-to="${ROOT_DIR}" "$f" 2>/dev/null || echo "$f")
|
||||
printf ' • %s\n' "$rel"
|
||||
if [[ "$used_db" == "yes" ]]; then
|
||||
if ! clang-tidy "$f" -p "${C_DIR}" --fix --format-style=file --quiet >/dev/null 2>&1; then
|
||||
failures=$((failures+1))
|
||||
fi
|
||||
else
|
||||
if ! clang-tidy "$f" --fix --format-style=file --quiet -- -std=c2x -I"$(dirname "$f")" -I"${C_DIR}" >/dev/null 2>&1; then
|
||||
failures=$((failures+1))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [[ $failures -gt 0 ]]; then
|
||||
warn "clang-tidy auto-fix encountered $failures issue(s); manual review may be required"
|
||||
else
|
||||
ok "clang-tidy auto-fix pass completed"
|
||||
fi
|
||||
}
|
||||
|
||||
apply_autofix() {
|
||||
if [[ "$AUTOFIX" == "0" ]]; then
|
||||
info "Automatic fixes disabled (LINT_AUTOFIX=0)"
|
||||
return
|
||||
fi
|
||||
info "Automatic fixes enabled (LINT_AUTOFIX=${AUTOFIX})"
|
||||
apply_clang_format_fix
|
||||
apply_clang_tidy_fix
|
||||
# Refresh file lists in case new files were introduced by fixes
|
||||
collect_files
|
||||
}
|
||||
|
||||
run_clang_format() {
|
||||
@ -197,9 +269,7 @@ run_clang_tidy() {
|
||||
info "Running clang-tidy on .c files"
|
||||
local db="${C_DIR}/compile_commands.json"
|
||||
local used_db="no"
|
||||
local files=()
|
||||
while IFS= read -r -d '' f; do files+=("$f"); done < <(find "${C_DIR}" -type f -name '*.c' -print0)
|
||||
if [[ ${#files[@]} -eq 0 ]]; then
|
||||
if [[ ${#C_SOURCES[@]} -eq 0 ]]; then
|
||||
warn "No .c files for clang-tidy"
|
||||
return
|
||||
fi
|
||||
@ -212,7 +282,7 @@ run_clang_tidy() {
|
||||
fi
|
||||
fi
|
||||
local failures=0
|
||||
for f in "${files[@]}"; do
|
||||
for f in "${C_SOURCES[@]}"; do
|
||||
if [[ "$used_db" == "yes" ]]; then
|
||||
clang-tidy "$f" -p "${C_DIR}" --quiet || failures=$((failures+1))
|
||||
else
|
||||
@ -266,6 +336,7 @@ main() {
|
||||
install_tools
|
||||
ensure_configs
|
||||
collect_files
|
||||
apply_autofix
|
||||
run_clang_format
|
||||
run_cppcheck
|
||||
run_clang_tidy
|
||||
|
||||
12
C/misc/generatingWordsEndingWIthalka.c
Normal file
12
C/misc/generatingWordsEndingWIthalka.c
Normal file
@ -0,0 +1,12 @@
|
||||
#include <stdio.h>
|
||||
|
||||
const int NUMBER_FOR_POLISH_SMALL_L = 136;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
for (char i = 'a'; i < 'z' + 1; ++i)
|
||||
{
|
||||
printf("%ca%cka\n", i, NUMBER_FOR_POLISH_SMALL_L);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
349
C/misc/randomJPG/generate_images.c
Normal file
349
C/misc/randomJPG/generate_images.c
Normal file
@ -0,0 +1,349 @@
|
||||
#include <errno.h>
|
||||
#include <jpeglib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char r, g, b;
|
||||
} RGB;
|
||||
|
||||
void print_usage(const char *program_name)
|
||||
{
|
||||
printf("Usage: %s [options] <num_images> <size> <block_size> <quality> <output_path> <format> "
|
||||
"<color1> ... <colorN>\n",
|
||||
program_name);
|
||||
printf("Options:\n");
|
||||
printf(" -h, --help Show this help message and exit\n");
|
||||
printf("Arguments:\n");
|
||||
printf(" <num_images> Number of images to generate (default: 1)\n");
|
||||
printf(" <size> Size of each image (default: 1000)\n");
|
||||
printf(" <block_size> Size of each block (default: 25)\n");
|
||||
printf(" <quality> Quality of the output image (default: 100)\n");
|
||||
printf(" <output_path> Path to save the output image (default: output.png)\n");
|
||||
printf(" <format> Output format (jpeg or bmp, default: jpeg)\n");
|
||||
printf(" <color1> ... <colorN> List of colors in hex format (default: #000000 and #FFFFFF)\n");
|
||||
}
|
||||
|
||||
void create_folder_if_not_exists(const char *folder)
|
||||
{
|
||||
struct stat st = {0};
|
||||
if (stat(folder, &st) == -1)
|
||||
{
|
||||
if (mkdir(folder, 0700) != 0)
|
||||
{
|
||||
fprintf(stderr, "Error creating directory: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void generate_image_filename(char *unique_output_path, size_t size, const char *folder,
|
||||
int image_index, const char *format)
|
||||
{
|
||||
snprintf(unique_output_path, size, "%s/bloated_image_%d.%s", folder, image_index, format);
|
||||
}
|
||||
|
||||
unsigned char *allocate_image_buffer(int size)
|
||||
{
|
||||
unsigned char *image_buffer = malloc(size * size * 3);
|
||||
if (!image_buffer)
|
||||
{
|
||||
fprintf(stderr, "Error allocating memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return image_buffer;
|
||||
}
|
||||
|
||||
void fill_image_with_colors(unsigned char *image_buffer, int size, RGB *color_list, int num_colors,
|
||||
int block_size)
|
||||
{
|
||||
for (int y = 0; y < size; y += block_size)
|
||||
{
|
||||
for (int x = 0; x < size; x += block_size)
|
||||
{
|
||||
RGB color = color_list[rand() % num_colors];
|
||||
for (int i = 0; i < block_size; ++i)
|
||||
{
|
||||
for (int j = 0; j < block_size; ++j)
|
||||
{
|
||||
int index = ((y + i) * size + (x + j)) * 3;
|
||||
image_buffer[index] = color.r;
|
||||
image_buffer[index + 1] = color.g;
|
||||
image_buffer[index + 2] = color.b;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handle_error(FILE *outfile, unsigned char *image_buffer)
|
||||
{
|
||||
if (!outfile)
|
||||
{
|
||||
fprintf(stderr, "Error opening output file: %s\n", strerror(errno));
|
||||
free(image_buffer);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void setup_compression(struct jpeg_compress_struct *cinfo, struct jpeg_error_mgr *jerr,
|
||||
FILE *outfile, int size, int quality)
|
||||
{
|
||||
cinfo->err = jpeg_std_error(jerr);
|
||||
jpeg_create_compress(cinfo);
|
||||
jpeg_stdio_dest(cinfo, outfile);
|
||||
|
||||
cinfo->image_width = size;
|
||||
cinfo->image_height = size;
|
||||
cinfo->input_components = 3;
|
||||
cinfo->in_color_space = JCS_RGB;
|
||||
|
||||
jpeg_set_defaults(cinfo);
|
||||
jpeg_set_quality(cinfo, quality, TRUE);
|
||||
jpeg_start_compress(cinfo, TRUE);
|
||||
}
|
||||
|
||||
void write_scanlines(struct jpeg_compress_struct *cinfo, unsigned char *image_buffer, int size)
|
||||
{
|
||||
JSAMPROW row_pointer;
|
||||
while (cinfo->next_scanline < cinfo->image_height)
|
||||
{
|
||||
row_pointer = (JSAMPROW)&image_buffer[cinfo->next_scanline * size * 3];
|
||||
jpeg_write_scanlines(cinfo, &row_pointer, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void finalize_compression(struct jpeg_compress_struct *cinfo, FILE *outfile)
|
||||
{
|
||||
jpeg_finish_compress(cinfo);
|
||||
fclose(outfile);
|
||||
jpeg_destroy_compress(cinfo);
|
||||
}
|
||||
|
||||
void save_image_as_jpeg(unsigned char *image_buffer, int size, const char *unique_output_path,
|
||||
int quality)
|
||||
{
|
||||
struct jpeg_compress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
FILE *outfile = fopen(unique_output_path, "wb");
|
||||
|
||||
handle_error(outfile, image_buffer);
|
||||
setup_compression(&cinfo, &jerr, outfile, size, quality);
|
||||
write_scanlines(&cinfo, image_buffer, size);
|
||||
finalize_compression(&cinfo, outfile);
|
||||
|
||||
free(image_buffer);
|
||||
}
|
||||
|
||||
void save_image_as_bmp(unsigned char *image_buffer, int size, const char *unique_output_path)
|
||||
{
|
||||
FILE *outfile = fopen(unique_output_path, "wb");
|
||||
handle_error(outfile, image_buffer);
|
||||
|
||||
// BMP Header
|
||||
unsigned char bmpfileheader[14] = {'B', 'M', 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0};
|
||||
unsigned char bmpinfoheader[40] = {40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 24, 0};
|
||||
int filesize = 54 + 3 * size * size;
|
||||
|
||||
bmpfileheader[2] = (unsigned char)(filesize);
|
||||
bmpfileheader[3] = (unsigned char)(filesize >> 8);
|
||||
bmpfileheader[4] = (unsigned char)(filesize >> 16);
|
||||
bmpfileheader[5] = (unsigned char)(filesize >> 24);
|
||||
|
||||
bmpinfoheader[4] = (unsigned char)(size);
|
||||
bmpinfoheader[5] = (unsigned char)(size >> 8);
|
||||
bmpinfoheader[6] = (unsigned char)(size >> 16);
|
||||
bmpinfoheader[7] = (unsigned char)(size >> 24);
|
||||
bmpinfoheader[8] = (unsigned char)(size);
|
||||
bmpinfoheader[9] = (unsigned char)(size >> 8);
|
||||
bmpinfoheader[10] = (unsigned char)(size >> 16);
|
||||
bmpinfoheader[11] = (unsigned char)(size >> 24);
|
||||
|
||||
fwrite(bmpfileheader, 1, 14, outfile);
|
||||
fwrite(bmpinfoheader, 1, 40, outfile);
|
||||
|
||||
// Write image data (in BMP format, rows are bottom to top)
|
||||
for (int y = size - 1; y >= 0; y--)
|
||||
{
|
||||
fwrite(image_buffer + (y * size * 3), 3, size, outfile);
|
||||
}
|
||||
|
||||
fclose(outfile);
|
||||
free(image_buffer);
|
||||
}
|
||||
|
||||
void generate_bloated_image(int size, RGB *color_list, int num_colors, int block_size,
|
||||
const char *output_path, int quality, int image_index,
|
||||
const char *folder, const char *format)
|
||||
{
|
||||
if (size % block_size != 0)
|
||||
{
|
||||
fprintf(stderr, "Size must be divisible by block_size\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
create_folder_if_not_exists(folder);
|
||||
|
||||
char unique_output_path[1024];
|
||||
generate_image_filename(unique_output_path, sizeof(unique_output_path), folder, image_index,
|
||||
format);
|
||||
|
||||
unsigned char *image_buffer = allocate_image_buffer(size);
|
||||
|
||||
fill_image_with_colors(image_buffer, size, color_list, num_colors, block_size);
|
||||
|
||||
if (strcmp(format, "jpeg") == 0)
|
||||
{
|
||||
save_image_as_jpeg(image_buffer, size, unique_output_path, quality);
|
||||
}
|
||||
else if (strcmp(format, "bmp") == 0)
|
||||
{
|
||||
save_image_as_bmp(image_buffer, size, unique_output_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Unsupported format: %s\n", format);
|
||||
free(image_buffer);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("Image %d saved to %s\n", image_index, unique_output_path);
|
||||
}
|
||||
|
||||
void allocate_color_list(int num_colors, RGB **color_list)
|
||||
{
|
||||
*color_list = malloc(num_colors * sizeof(RGB));
|
||||
if (!(*color_list))
|
||||
{
|
||||
fprintf(stderr, "Error allocating memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_single_color(const char *color_str, RGB *color)
|
||||
{
|
||||
unsigned int r, g, b;
|
||||
if (sscanf(color_str, "#%02x%02x%02x", &r, &g, &b) != 3)
|
||||
{
|
||||
fprintf(stderr, "Invalid color format: %s\n", color_str);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
*color = (RGB){r, g, b};
|
||||
}
|
||||
|
||||
void parse_color_list(int argc, char *argv[], int *num_colors, RGB **color_list)
|
||||
{
|
||||
*num_colors = argc - 7;
|
||||
allocate_color_list(*num_colors, color_list);
|
||||
for (int i = 0; i < *num_colors; ++i)
|
||||
{
|
||||
parse_single_color(argv[7 + i], &(*color_list)[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_default_colors(int *num_colors, RGB **color_list)
|
||||
{
|
||||
const char *default_colors[] = {"#000000", "#FFFFFF", "#0000FF", "#00FF00",
|
||||
"#00FFFF", "#FF0000", "#FF00FF", "#FFFF00"};
|
||||
*num_colors = sizeof(default_colors) / sizeof(default_colors[0]);
|
||||
allocate_color_list(*num_colors, color_list);
|
||||
for (int i = 0; i < *num_colors; ++i)
|
||||
{
|
||||
parse_single_color(default_colors[i], &(*color_list)[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_colors(int argc, char *argv[], int *num_colors, RGB **color_list)
|
||||
{
|
||||
if (argc > 7)
|
||||
{
|
||||
parse_color_list(argc, argv, num_colors, color_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
parse_default_colors(num_colors, color_list);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_arguments(int argc, char *argv[], int *num_images, int *size, int *block_size,
|
||||
int *quality, const char **output_path, const char **format)
|
||||
{
|
||||
// Default values
|
||||
*num_images = 1;
|
||||
*size = 1000;
|
||||
*block_size = 25;
|
||||
*quality = 100;
|
||||
*output_path = "output.png";
|
||||
*format = "jpeg";
|
||||
|
||||
if (argc > 1)
|
||||
*num_images = atoi(argv[1]);
|
||||
if (argc > 2)
|
||||
*size = atoi(argv[2]);
|
||||
if (argc > 3)
|
||||
*block_size = atoi(argv[3]);
|
||||
if (argc > 4)
|
||||
*quality = atoi(argv[4]);
|
||||
if (argc > 5)
|
||||
*output_path = argv[5];
|
||||
if (argc > 6)
|
||||
*format = argv[6];
|
||||
}
|
||||
|
||||
void create_output_folder(char *folder, size_t folder_size)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
struct tm *t = localtime(&now);
|
||||
strftime(folder, folder_size, "generated_images_%Y%m%d_%H%M%S", t);
|
||||
}
|
||||
|
||||
int handle_help_option(int argc, char *argv[])
|
||||
{
|
||||
if (argc > 1 && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0))
|
||||
{
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
srand(time(NULL));
|
||||
if (handle_help_option(argc, argv))
|
||||
{
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// Start time measurement
|
||||
clock_t start_time = clock();
|
||||
|
||||
int num_images, size, block_size, quality;
|
||||
const char *output_path, *format;
|
||||
parse_arguments(argc, argv, &num_images, &size, &block_size, &quality, &output_path, &format);
|
||||
|
||||
RGB *color_list;
|
||||
int num_colors;
|
||||
parse_colors(argc, argv, &num_colors, &color_list);
|
||||
|
||||
char folder[64];
|
||||
create_output_folder(folder, sizeof(folder));
|
||||
|
||||
for (int i = 1; i <= num_images; ++i)
|
||||
{
|
||||
generate_bloated_image(size, color_list, num_colors, block_size, output_path, quality, i,
|
||||
folder, format);
|
||||
}
|
||||
|
||||
free(color_list);
|
||||
|
||||
// End time measurement
|
||||
clock_t end_time = clock();
|
||||
double execution_time = (double)(end_time - start_time) / CLOCKS_PER_SEC;
|
||||
printf("Generated %d images in %f seconds!\n", num_images, execution_time);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@ -1,17 +1,21 @@
|
||||
#include <errno.h>
|
||||
#include <jpeglib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <jpeglib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
unsigned char r, g, b;
|
||||
} RGB;
|
||||
|
||||
void print_usage(const char* program_name) {
|
||||
printf("Usage: %s [options] <num_images> <size> <block_size> <quality> <output_path> <color1> ... <colorN>\n", program_name);
|
||||
void print_usage(const char *program_name)
|
||||
{
|
||||
printf("Usage: %s [options] <num_images> <size> <block_size> <quality> <output_path> <color1> "
|
||||
"... <colorN>\n",
|
||||
program_name);
|
||||
printf("Options:\n");
|
||||
printf(" -h, --help Show this help message and exit\n");
|
||||
printf("Arguments:\n");
|
||||
@ -23,37 +27,50 @@ void print_usage(const char* program_name) {
|
||||
printf(" <color1> ... <colorN> List of colors in hex format (default: #000000 and #FFFFFF)\n");
|
||||
}
|
||||
|
||||
void create_folder_if_not_exists(const char* folder) {
|
||||
void create_folder_if_not_exists(const char *folder)
|
||||
{
|
||||
struct stat st = {0};
|
||||
if (stat(folder, &st) == -1) {
|
||||
if (mkdir(folder, 0700) != 0) {
|
||||
if (stat(folder, &st) == -1)
|
||||
{
|
||||
if (mkdir(folder, 0700) != 0)
|
||||
{
|
||||
fprintf(stderr, "Error creating directory: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void generate_image_filename(char* unique_output_path, size_t size, const char* folder, int image_index) {
|
||||
void generate_image_filename(char *unique_output_path, size_t size, const char *folder,
|
||||
int image_index)
|
||||
{
|
||||
snprintf(unique_output_path, size, "%s/bloated_image_%d.jpg", folder, image_index);
|
||||
}
|
||||
|
||||
unsigned char* allocate_image_buffer(int size) {
|
||||
unsigned char *allocate_image_buffer(int size)
|
||||
{
|
||||
unsigned char *image_buffer = malloc(size * size * 3);
|
||||
if (!image_buffer) {
|
||||
if (!image_buffer)
|
||||
{
|
||||
fprintf(stderr, "Error allocating memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return image_buffer;
|
||||
}
|
||||
|
||||
void fill_image_with_colors(unsigned char* image_buffer, int size, RGB* color_list, int num_colors, int block_size) {
|
||||
for (int y = 0; y < size; y += block_size) {
|
||||
for (int x = 0; x < size; x += block_size) {
|
||||
void fill_image_with_colors(unsigned char *image_buffer, int size, RGB *color_list, int num_colors,
|
||||
int block_size)
|
||||
{
|
||||
for (int y = 0; y < size; y += block_size)
|
||||
{
|
||||
for (int x = 0; x < size; x += block_size)
|
||||
{
|
||||
RGB color = color_list[rand() % num_colors];
|
||||
for (int i = 0; i < block_size; ++i) {
|
||||
for (int j = 0; j < block_size; ++j) {
|
||||
int index = ((y + i) * size + (x + j)) * 3;
|
||||
image_buffer[index] = color.r;
|
||||
for (int i = 0; i < block_size; ++i)
|
||||
{
|
||||
for (int j = 0; j < block_size; ++j)
|
||||
{
|
||||
int index = ((y + i) * size + (x + j)) * 3;
|
||||
image_buffer[index] = color.r;
|
||||
image_buffer[index + 1] = color.g;
|
||||
image_buffer[index + 2] = color.b;
|
||||
}
|
||||
@ -62,48 +79,56 @@ void fill_image_with_colors(unsigned char* image_buffer, int size, RGB* color_li
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void handle_error(FILE *outfile, unsigned char *image_buffer) {
|
||||
if (!outfile) {
|
||||
void handle_error(FILE *outfile, unsigned char *image_buffer)
|
||||
{
|
||||
if (!outfile)
|
||||
{
|
||||
fprintf(stderr, "Error opening output file: %s\n", strerror(errno));
|
||||
free(image_buffer);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void setup_compression(struct jpeg_compress_struct *cinfo, struct jpeg_error_mgr *jerr, FILE *outfile, int size, int quality) {
|
||||
void setup_compression(struct jpeg_compress_struct *cinfo, struct jpeg_error_mgr *jerr,
|
||||
FILE *outfile, int size, int quality)
|
||||
{
|
||||
cinfo->err = jpeg_std_error(jerr);
|
||||
jpeg_create_compress(cinfo);
|
||||
jpeg_stdio_dest(cinfo, outfile);
|
||||
|
||||
cinfo->image_width = size;
|
||||
cinfo->image_height = size;
|
||||
cinfo->image_width = size;
|
||||
cinfo->image_height = size;
|
||||
cinfo->input_components = 3;
|
||||
cinfo->in_color_space = JCS_RGB;
|
||||
cinfo->in_color_space = JCS_RGB;
|
||||
|
||||
jpeg_set_defaults(cinfo);
|
||||
jpeg_set_quality(cinfo, quality, TRUE);
|
||||
jpeg_start_compress(cinfo, TRUE);
|
||||
}
|
||||
|
||||
void write_scanlines(struct jpeg_compress_struct *cinfo, unsigned char *image_buffer, int size) {
|
||||
void write_scanlines(struct jpeg_compress_struct *cinfo, unsigned char *image_buffer, int size)
|
||||
{
|
||||
JSAMPROW row_pointer;
|
||||
while (cinfo->next_scanline < cinfo->image_height) {
|
||||
row_pointer = (JSAMPROW) &image_buffer[cinfo->next_scanline * size * 3];
|
||||
while (cinfo->next_scanline < cinfo->image_height)
|
||||
{
|
||||
row_pointer = (JSAMPROW)&image_buffer[cinfo->next_scanline * size * 3];
|
||||
jpeg_write_scanlines(cinfo, &row_pointer, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void finalize_compression(struct jpeg_compress_struct *cinfo, FILE *outfile) {
|
||||
void finalize_compression(struct jpeg_compress_struct *cinfo, FILE *outfile)
|
||||
{
|
||||
jpeg_finish_compress(cinfo);
|
||||
fclose(outfile);
|
||||
jpeg_destroy_compress(cinfo);
|
||||
}
|
||||
|
||||
void save_image_as_jpeg(unsigned char* image_buffer, int size, const char* unique_output_path, int quality) {
|
||||
void save_image_as_jpeg(unsigned char *image_buffer, int size, const char *unique_output_path,
|
||||
int quality)
|
||||
{
|
||||
struct jpeg_compress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
FILE *outfile = fopen(unique_output_path, "wb");
|
||||
struct jpeg_error_mgr jerr;
|
||||
FILE *outfile = fopen(unique_output_path, "wb");
|
||||
|
||||
handle_error(outfile, image_buffer);
|
||||
setup_compression(&cinfo, &jerr, outfile, size, quality);
|
||||
@ -113,8 +138,12 @@ void save_image_as_jpeg(unsigned char* image_buffer, int size, const char* uniqu
|
||||
free(image_buffer);
|
||||
}
|
||||
|
||||
void generate_bloated_jpeg(int size, RGB* color_list, int num_colors, int block_size, const char* output_path, int quality, int image_index, const char* folder) {
|
||||
if (size % block_size != 0) {
|
||||
void generate_bloated_jpeg(int size, RGB *color_list, int num_colors, int block_size,
|
||||
const char *output_path, int quality, int image_index,
|
||||
const char *folder)
|
||||
{
|
||||
if (size % block_size != 0)
|
||||
{
|
||||
fprintf(stderr, "Size must be divisible by block_size\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@ -124,7 +153,7 @@ void generate_bloated_jpeg(int size, RGB* color_list, int num_colors, int block_
|
||||
char unique_output_path[1024];
|
||||
generate_image_filename(unique_output_path, sizeof(unique_output_path), folder, image_index);
|
||||
|
||||
unsigned char* image_buffer = allocate_image_buffer(size);
|
||||
unsigned char *image_buffer = allocate_image_buffer(size);
|
||||
|
||||
fill_image_with_colors(image_buffer, size, color_list, num_colors, block_size);
|
||||
|
||||
@ -133,106 +162,132 @@ void generate_bloated_jpeg(int size, RGB* color_list, int num_colors, int block_
|
||||
printf("Image %d saved to %s\n", image_index, unique_output_path);
|
||||
}
|
||||
|
||||
void allocate_color_list(int num_colors, RGB** color_list) {
|
||||
void allocate_color_list(int num_colors, RGB **color_list)
|
||||
{
|
||||
*color_list = malloc(num_colors * sizeof(RGB));
|
||||
if (!(*color_list)) {
|
||||
if (!(*color_list))
|
||||
{
|
||||
fprintf(stderr, "Error allocating memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_single_color(const char* color_str, RGB* color) {
|
||||
void parse_single_color(const char *color_str, RGB *color)
|
||||
{
|
||||
unsigned int r, g, b;
|
||||
if (sscanf(color_str, "#%02x%02x%02x", &r, &g, &b) != 3) {
|
||||
if (sscanf(color_str, "#%02x%02x%02x", &r, &g, &b) != 3)
|
||||
{
|
||||
fprintf(stderr, "Invalid color format: %s\n", color_str);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
*color = (RGB){ r, g, b };
|
||||
*color = (RGB){r, g, b};
|
||||
}
|
||||
|
||||
void parse_color_list(int argc, char* argv[], int* num_colors, RGB** color_list) {
|
||||
void parse_color_list(int argc, char *argv[], int *num_colors, RGB **color_list)
|
||||
{
|
||||
*num_colors = argc - 6;
|
||||
allocate_color_list(*num_colors, color_list);
|
||||
for (int i = 0; i < *num_colors; ++i) {
|
||||
for (int i = 0; i < *num_colors; ++i)
|
||||
{
|
||||
parse_single_color(argv[6 + i], &(*color_list)[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_default_colors(int* num_colors, RGB** color_list) {
|
||||
const char* default_colors[] = { "#000000", "#FFFFFF" };
|
||||
*num_colors = sizeof(default_colors) / sizeof(default_colors[0]);
|
||||
void parse_default_colors(int *num_colors, RGB **color_list)
|
||||
{
|
||||
const char *default_colors[] = {"#000000", "#FFFFFF"};
|
||||
*num_colors = sizeof(default_colors) / sizeof(default_colors[0]);
|
||||
allocate_color_list(*num_colors, color_list);
|
||||
for (int i = 0; i < *num_colors; ++i) {
|
||||
for (int i = 0; i < *num_colors; ++i)
|
||||
{
|
||||
parse_single_color(default_colors[i], &(*color_list)[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_colors(int argc, char* argv[], int* num_colors, RGB** color_list) {
|
||||
if (argc > 6) {
|
||||
void parse_colors(int argc, char *argv[], int *num_colors, RGB **color_list)
|
||||
{
|
||||
if (argc > 6)
|
||||
{
|
||||
parse_color_list(argc, argv, num_colors, color_list);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
parse_default_colors(num_colors, color_list);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_arguments(int argc, char* argv[], int* num_images, int* size, int* block_size, int* quality, const char** output_path) {
|
||||
void parse_arguments(int argc, char *argv[], int *num_images, int *size, int *block_size,
|
||||
int *quality, const char **output_path)
|
||||
{
|
||||
// Default values
|
||||
*num_images = 1;
|
||||
*size = 1000;
|
||||
*block_size = 25;
|
||||
*quality = 100;
|
||||
*num_images = 1;
|
||||
*size = 1000;
|
||||
*block_size = 25;
|
||||
*quality = 100;
|
||||
*output_path = "output.png";
|
||||
|
||||
if (argc > 1) *num_images = atoi(argv[1]);
|
||||
if (argc > 2) *size = atoi(argv[2]);
|
||||
if (argc > 3) *block_size = atoi(argv[3]);
|
||||
if (argc > 4) *quality = atoi(argv[4]);
|
||||
if (argc > 5) *output_path = argv[5];
|
||||
if (argc > 1)
|
||||
*num_images = atoi(argv[1]);
|
||||
if (argc > 2)
|
||||
*size = atoi(argv[2]);
|
||||
if (argc > 3)
|
||||
*block_size = atoi(argv[3]);
|
||||
if (argc > 4)
|
||||
*quality = atoi(argv[4]);
|
||||
if (argc > 5)
|
||||
*output_path = argv[5];
|
||||
}
|
||||
|
||||
void create_output_folder(char *folder, size_t folder_size) {
|
||||
time_t now = time(NULL);
|
||||
struct tm *t = localtime(&now);
|
||||
void create_output_folder(char *folder, size_t folder_size)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
struct tm *t = localtime(&now);
|
||||
strftime(folder, folder_size, "generated_images_%Y%m%d_%H%M%S", t);
|
||||
}
|
||||
|
||||
int handle_help_option(int argc, char *argv[]) {
|
||||
if (argc > 1 && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0)) {
|
||||
int handle_help_option(int argc, char *argv[])
|
||||
{
|
||||
if (argc > 1 && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0))
|
||||
{
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
srand(time(NULL));
|
||||
if (handle_help_option(argc, argv)) {
|
||||
if (handle_help_option(argc, argv))
|
||||
{
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// Start time measurement
|
||||
clock_t start_time = clock();
|
||||
|
||||
int num_images, size, block_size, quality;
|
||||
int num_images, size, block_size, quality;
|
||||
const char *output_path;
|
||||
parse_arguments(argc, argv, &num_images, &size, &block_size, &quality, &output_path);
|
||||
|
||||
RGB *color_list;
|
||||
int num_colors;
|
||||
int num_colors;
|
||||
parse_colors(argc, argv, &num_colors, &color_list);
|
||||
|
||||
char folder[64];
|
||||
create_output_folder(folder, sizeof(folder));
|
||||
|
||||
for (int i = 1; i <= num_images; ++i) {
|
||||
generate_bloated_jpeg(size, color_list, num_colors, block_size, output_path, quality, i, folder);
|
||||
for (int i = 1; i <= num_images; ++i)
|
||||
{
|
||||
generate_bloated_jpeg(size, color_list, num_colors, block_size, output_path, quality, i,
|
||||
folder);
|
||||
}
|
||||
|
||||
free(color_list);
|
||||
|
||||
// End time measurement
|
||||
clock_t end_time = clock();
|
||||
double execution_time = (double)(end_time - start_time) / CLOCKS_PER_SEC;
|
||||
clock_t end_time = clock();
|
||||
double execution_time = (double)(end_time - start_time) / CLOCKS_PER_SEC;
|
||||
printf("Generated %d images in %f seconds!\n", num_images, execution_time);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
115
C/misc/split/main.c
Normal file
115
C/misc/split/main.c
Normal file
@ -0,0 +1,115 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Function to calculate symmetric weights for both even and odd N
|
||||
void calculate_symmetric_weights(int N, double middle_weight, const double *factors,
|
||||
double *weights)
|
||||
{
|
||||
int half_N = N / 2;
|
||||
int i = 0;
|
||||
weights[half_N] = middle_weight; // Middle value for symmetry
|
||||
|
||||
// Calculate left side weights
|
||||
if (factors)
|
||||
{
|
||||
for (i = 0; i < half_N; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
weights[half_N - i - 1] = middle_weight + factors[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
weights[half_N - i - 1] = weights[half_N - i] + factors[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < half_N; i++)
|
||||
{
|
||||
weights[half_N - i - 1] = middle_weight - (i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Mirror left side weights to right side
|
||||
for (i = 0; i < half_N; i++)
|
||||
{
|
||||
weights[half_N + i + 1] = weights[half_N - i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
// Function to scale the weights so that their sum is proportional to X
|
||||
void scale_to_total(double X, const double *weights, int N, double *distances)
|
||||
{
|
||||
double total_weight = 0;
|
||||
int i = 0;
|
||||
|
||||
// Calculate the total weight
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
total_weight += weights[i];
|
||||
}
|
||||
|
||||
double base_unit = X / total_weight;
|
||||
|
||||
// Scale weights
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
distances[i] = base_unit * weights[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Function to split X into N parts symmetrically
|
||||
void split_x_into_n_symmetrically(double X, int N, double *factors, double *distances)
|
||||
{
|
||||
double *weights = (double *)malloc(N * sizeof(double));
|
||||
|
||||
calculate_symmetric_weights(N, 1.0, factors, weights);
|
||||
scale_to_total(X, weights, N, distances);
|
||||
|
||||
free(weights);
|
||||
}
|
||||
|
||||
// Function to split X into N parts, with a specific middle value
|
||||
void split_x_into_n_middle(double X, int N, double middle_value, double *distances)
|
||||
{
|
||||
double *weights = (double *)malloc(N * sizeof(double));
|
||||
|
||||
calculate_symmetric_weights(N, middle_value, NULL, weights);
|
||||
scale_to_total(X, weights, N, distances);
|
||||
|
||||
free(weights);
|
||||
}
|
||||
|
||||
// Example usage
|
||||
int main(void)
|
||||
{
|
||||
int N = 5;
|
||||
double X = 100;
|
||||
double middle_value = 5.0;
|
||||
double distances[5];
|
||||
|
||||
// Example usage for split_x_into_n_middle
|
||||
split_x_into_n_middle(X, N, middle_value, distances);
|
||||
|
||||
printf("Split values (with middle value = %.2f):\n", middle_value);
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
printf("%.2f ", distances[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Example usage for split_x_into_n_symmetrically
|
||||
double factors[2] = {1.0, 2.0};
|
||||
split_x_into_n_symmetrically(X, N, factors, distances);
|
||||
|
||||
printf("Split values (symmetric with factors):\n");
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
printf("%.2f ", distances[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,296 +1,670 @@
|
||||
#include "chess.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static const int knight_offsets[8] = {15, 17, -15, -17, 10, -10, 6, -6};
|
||||
static const int bishop_dirs[4] = {9, 7, -9, -7};
|
||||
static const int rook_dirs[4] = {8, -8, 1, -1};
|
||||
static const int king_dirs[8] = {8,-8,1,-1,9,7,-9,-7};
|
||||
static const int bishop_dirs[4] = {9, 7, -9, -7};
|
||||
static const int rook_dirs[4] = {8, -8, 1, -1};
|
||||
static const int king_dirs[8] = {8, -8, 1, -1, 9, 7, -9, -7};
|
||||
|
||||
static inline int file_of(int sq){return sq%8;}
|
||||
static inline int rank_of(int sq){return sq/8;}
|
||||
static inline bool on_board(int sq){return sq>=0 && sq<64;}
|
||||
static inline bool same_color(char a, char b){return (isupper(a)&&isupper(b))||(islower(a)&&islower(b));}
|
||||
static inline bool is_white(char p){return isupper((unsigned char)p);}
|
||||
static inline int file_of(int sq) { return sq % 8; }
|
||||
static inline int rank_of(int sq) { return sq / 8; }
|
||||
static inline bool on_board(int sq) { return sq >= 0 && sq < 64; }
|
||||
static inline bool same_color(char a, char b)
|
||||
{
|
||||
return (isupper(a) && isupper(b)) || (islower(a) && islower(b));
|
||||
}
|
||||
static inline bool is_white(char p) { return isupper((unsigned char)p); }
|
||||
|
||||
void chess_init_start(Position *pos){
|
||||
typedef struct
|
||||
{
|
||||
int from;
|
||||
int to;
|
||||
char promo;
|
||||
char captured;
|
||||
} MoveCandidate;
|
||||
|
||||
void chess_init_start(Position *pos)
|
||||
{
|
||||
// a1..h1 (0..7) are white back rank; rank 8 at indexes 56..63 are black back rank
|
||||
const char *start = "RNBQKBNRPPPPPPPP................................pppppppprnbqkbnr";
|
||||
for (int i=0;i<64;i++) pos->board[i] = start[i];
|
||||
for (int i = 0; i < 64; i++)
|
||||
pos->board[i] = start[i];
|
||||
pos->white_to_move = true;
|
||||
pos->castle_wk = pos->castle_wq = pos->castle_bk = pos->castle_bq = true;
|
||||
pos->ep_square = -1; pos->halfmove_clock = 0; pos->fullmove_number = 1;
|
||||
pos->ep_square = -1;
|
||||
pos->halfmove_clock = 0;
|
||||
pos->fullmove_number = 1;
|
||||
}
|
||||
|
||||
void chess_copy(Position *dst, const Position *src){ *dst = *src; }
|
||||
void chess_copy(Position *dst, const Position *src) { *dst = *src; }
|
||||
|
||||
static bool is_empty(const Position *p, int sq){ return p->board[sq]=='.'; }
|
||||
static bool is_empty(const Position *p, int sq) { return p->board[sq] == '.'; }
|
||||
|
||||
bool chess_square_attacked(const Position *pos, int sq, bool by_white){
|
||||
bool chess_square_attacked(const Position *pos, int sq, bool by_white)
|
||||
{
|
||||
// pawns
|
||||
int r = rank_of(sq), f = file_of(sq);
|
||||
if (by_white){
|
||||
int s1 = (r-1)*8 + (f-1); if (f>0 && r>0 && on_board(s1) && pos->board[s1]=='P') return true;
|
||||
int s2 = (r-1)*8 + (f+1); if (f<7 && r>0 && on_board(s2) && pos->board[s2]=='P') return true;
|
||||
} else {
|
||||
int s1 = (r+1)*8 + (f-1); if (f>0 && r<7 && on_board(s1) && pos->board[s1]=='p') return true;
|
||||
int s2 = (r+1)*8 + (f+1); if (f<7 && r<7 && on_board(s2) && pos->board[s2]=='p') return true;
|
||||
if (by_white)
|
||||
{
|
||||
int s1 = (r - 1) * 8 + (f - 1);
|
||||
if (f > 0 && r > 0 && on_board(s1) && pos->board[s1] == 'P')
|
||||
return true;
|
||||
int s2 = (r - 1) * 8 + (f + 1);
|
||||
if (f < 7 && r > 0 && on_board(s2) && pos->board[s2] == 'P')
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int s1 = (r + 1) * 8 + (f - 1);
|
||||
if (f > 0 && r < 7 && on_board(s1) && pos->board[s1] == 'p')
|
||||
return true;
|
||||
int s2 = (r + 1) * 8 + (f + 1);
|
||||
if (f < 7 && r < 7 && on_board(s2) && pos->board[s2] == 'p')
|
||||
return true;
|
||||
}
|
||||
// knights
|
||||
for (int i=0;i<8;i++){
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
int t = sq + knight_offsets[i];
|
||||
if (!on_board(t)) continue;
|
||||
int df = file_of(t)-f; int dr = rank_of(t)-r; if (df< -2||df>2||dr<-2||dr>2) continue; // edge wrap guard
|
||||
if (!on_board(t))
|
||||
continue;
|
||||
int df = file_of(t) - f;
|
||||
int dr = rank_of(t) - r;
|
||||
if (df < -2 || df > 2 || dr < -2 || dr > 2)
|
||||
continue; // edge wrap guard
|
||||
char pc = pos->board[t];
|
||||
if (by_white && pc=='N') return true;
|
||||
if (!by_white && pc=='n') return true;
|
||||
if (by_white && pc == 'N')
|
||||
return true;
|
||||
if (!by_white && pc == 'n')
|
||||
return true;
|
||||
}
|
||||
// bishops/queens
|
||||
for (int d=0; d<4; d++){
|
||||
int off = bishop_dirs[d]; int t = sq + off;
|
||||
while (on_board(t) && abs(file_of(t)-f)==abs(rank_of(t)-r)){
|
||||
for (int d = 0; d < 4; d++)
|
||||
{
|
||||
int off = bishop_dirs[d];
|
||||
int t = sq + off;
|
||||
while (on_board(t) && abs(file_of(t) - f) == abs(rank_of(t) - r))
|
||||
{
|
||||
char pc = pos->board[t];
|
||||
if (pc!='.') { if (by_white && (pc=='B'||pc=='Q')) return true; if (!by_white && (pc=='b'||pc=='q')) return true; break; }
|
||||
if (pc != '.')
|
||||
{
|
||||
if (by_white && (pc == 'B' || pc == 'Q'))
|
||||
return true;
|
||||
if (!by_white && (pc == 'b' || pc == 'q'))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
t += off;
|
||||
}
|
||||
}
|
||||
// rooks/queens
|
||||
for (int d=0; d<4; d++){
|
||||
int off = rook_dirs[d]; int t = sq + off;
|
||||
while (on_board(t) && (file_of(t)==f || rank_of(t)==r)){
|
||||
for (int d = 0; d < 4; d++)
|
||||
{
|
||||
int off = rook_dirs[d];
|
||||
int t = sq + off;
|
||||
while (on_board(t) && (file_of(t) == f || rank_of(t) == r))
|
||||
{
|
||||
char pc = pos->board[t];
|
||||
if (pc!='.') { if (by_white && (pc=='R'||pc=='Q')) return true; if (!by_white && (pc=='r'||pc=='q')) return true; break; }
|
||||
if (pc != '.')
|
||||
{
|
||||
if (by_white && (pc == 'R' || pc == 'Q'))
|
||||
return true;
|
||||
if (!by_white && (pc == 'r' || pc == 'q'))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
t += off;
|
||||
}
|
||||
}
|
||||
// king
|
||||
for (int i=0;i<8;i++){
|
||||
int t=sq+king_dirs[i]; if (!on_board(t)) continue; if (abs(file_of(t)-f)>1||abs(rank_of(t)-r)>1) continue;
|
||||
char pc = pos->board[t]; if (by_white && pc=='K') return true; if (!by_white && pc=='k') return true;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
int t = sq + king_dirs[i];
|
||||
if (!on_board(t))
|
||||
continue;
|
||||
if (abs(file_of(t) - f) > 1 || abs(rank_of(t) - r) > 1)
|
||||
continue;
|
||||
char pc = pos->board[t];
|
||||
if (by_white && pc == 'K')
|
||||
return true;
|
||||
if (!by_white && pc == 'k')
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool chess_is_in_check(const Position *pos, bool white){
|
||||
int ks = -1; char k = white? 'K' : 'k';
|
||||
for (int i=0;i<64;i++) if (pos->board[i]==k) { ks = i; break; }
|
||||
if (ks==-1) return false; // malformed
|
||||
bool chess_is_in_check(const Position *pos, bool white)
|
||||
{
|
||||
int ks = -1;
|
||||
char k = white ? 'K' : 'k';
|
||||
for (int i = 0; i < 64; i++)
|
||||
if (pos->board[i] == k)
|
||||
{
|
||||
ks = i;
|
||||
break;
|
||||
}
|
||||
if (ks == -1)
|
||||
return false; // malformed
|
||||
return chess_square_attacked(pos, ks, !white);
|
||||
}
|
||||
|
||||
static void add_move_if_legal(const Position *pos, int from, int to, char promo, Move *out, size_t *n, size_t max){
|
||||
if (*n >= max) return;
|
||||
Position tmp; chess_copy(&tmp, pos);
|
||||
Move m = {0}; m.from=from; m.to=to; m.promo=promo; m.moved=pos->board[from]; m.captured=pos->board[to];
|
||||
int prev_ep = tmp.ep_square; m.prev_ep = prev_ep; m.prev_wk=tmp.castle_wk; m.prev_wq=tmp.castle_wq; m.prev_bk=tmp.castle_bk; m.prev_bq=tmp.castle_bq; m.prev_halfmove=tmp.halfmove_clock;
|
||||
if (!chess_make_move(&tmp, &m)) return;
|
||||
if (chess_is_in_check(&tmp, !tmp.white_to_move)) return; // after make, side switched
|
||||
static void add_move_if_legal(const Position *pos, MoveCandidate candidate, Move *out, size_t *n,
|
||||
size_t max)
|
||||
{
|
||||
if (*n >= max)
|
||||
return;
|
||||
Position tmp;
|
||||
chess_copy(&tmp, pos);
|
||||
char captured_piece = candidate.captured ? candidate.captured : pos->board[candidate.to];
|
||||
Move m = {0};
|
||||
m.from = candidate.from;
|
||||
m.to = candidate.to;
|
||||
m.promo = candidate.promo;
|
||||
m.moved = pos->board[candidate.from];
|
||||
m.captured = captured_piece;
|
||||
int prev_ep = tmp.ep_square;
|
||||
m.prev_ep = prev_ep;
|
||||
m.prev_wk = tmp.castle_wk;
|
||||
m.prev_wq = tmp.castle_wq;
|
||||
m.prev_bk = tmp.castle_bk;
|
||||
m.prev_bq = tmp.castle_bq;
|
||||
m.prev_halfmove = tmp.halfmove_clock;
|
||||
if (!chess_make_move(&tmp, &m))
|
||||
return;
|
||||
if (chess_is_in_check(&tmp, !tmp.white_to_move))
|
||||
return; // after make, side switched
|
||||
out[(*n)++] = m; // store pseudo move with added flags from make_move
|
||||
}
|
||||
|
||||
size_t chess_generate_legal_moves(const Position *pos, Move *out, size_t max){
|
||||
size_t n=0;
|
||||
bool white = pos->white_to_move;
|
||||
for (int sq=0; sq<64; sq++){
|
||||
char p = pos->board[sq]; if (p=='.') continue; if (white != is_white(p)) continue;
|
||||
int f=file_of(sq), r=rank_of(sq);
|
||||
switch (tolower(p)){
|
||||
case 'p': {
|
||||
int dir = white? 8 : -8; int start_rank = white? 1 : 6; int prom_rank = white? 6 : 1;
|
||||
int one = sq + dir;
|
||||
if (on_board(one) && is_empty(pos, one)){
|
||||
if (r==prom_rank){
|
||||
const char *pr = white? "QRBN" : "qrbn";
|
||||
for (int i=0;i<4;i++) add_move_if_legal(pos, sq, one, pr[i], out, &n, max);
|
||||
} else add_move_if_legal(pos, sq, one, 0, out, &n, max);
|
||||
// two
|
||||
int two = sq + 2*dir; if (r==start_rank && is_empty(pos, two)) add_move_if_legal(pos, sq, two, 0, out, &n, max);
|
||||
size_t chess_generate_legal_moves(const Position *pos, Move *out, size_t max)
|
||||
{
|
||||
size_t n = 0;
|
||||
bool white = pos->white_to_move;
|
||||
for (int sq = 0; sq < 64; sq++)
|
||||
{
|
||||
char p = pos->board[sq];
|
||||
if (p == '.')
|
||||
continue;
|
||||
if (white != is_white(p))
|
||||
continue;
|
||||
int f = file_of(sq), r = rank_of(sq);
|
||||
switch (tolower(p))
|
||||
{
|
||||
case 'p':
|
||||
{
|
||||
int dir = white ? 8 : -8;
|
||||
int start_rank = white ? 1 : 6;
|
||||
int prom_rank = white ? 6 : 1;
|
||||
int one = sq + dir;
|
||||
if (on_board(one) && is_empty(pos, one))
|
||||
{
|
||||
if (r == prom_rank)
|
||||
{
|
||||
const char *pr = white ? "QRBN" : "qrbn";
|
||||
for (int i = 0; i < 4; i++)
|
||||
add_move_if_legal(pos,
|
||||
(MoveCandidate){.from = sq, .to = one, .promo = pr[i]},
|
||||
out, &n, max);
|
||||
}
|
||||
// captures
|
||||
int caps[2] = { dir+1, dir-1 };
|
||||
for (int i=0;i<2;i++){
|
||||
int t = sq + caps[i]; if (!on_board(t)) continue; if (abs(file_of(t)-f)!=1) continue;
|
||||
if (!is_empty(pos, t) && !same_color(pos->board[sq], pos->board[t])){
|
||||
if (r==prom_rank){ const char *pr = white? "QRBN" : "qrbn"; for (int j=0;j<4;j++) add_move_if_legal(pos, sq, t, pr[j], out, &n, max); }
|
||||
else add_move_if_legal(pos, sq, t, 0, out, &n, max);
|
||||
else
|
||||
{
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = sq, .to = one, .promo = 0}, out,
|
||||
&n, max);
|
||||
}
|
||||
// two
|
||||
int two = sq + 2 * dir;
|
||||
if (r == start_rank && is_empty(pos, two))
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = sq, .to = two, .promo = 0}, out,
|
||||
&n, max);
|
||||
}
|
||||
// captures
|
||||
int caps[2] = {dir + 1, dir - 1};
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
int t = sq + caps[i];
|
||||
if (!on_board(t))
|
||||
continue;
|
||||
if (abs(file_of(t) - f) != 1)
|
||||
continue;
|
||||
if (!is_empty(pos, t) && !same_color(pos->board[sq], pos->board[t]))
|
||||
{
|
||||
if (r == prom_rank)
|
||||
{
|
||||
const char *pr = white ? "QRBN" : "qrbn";
|
||||
for (int j = 0; j < 4; j++)
|
||||
add_move_if_legal(pos,
|
||||
(MoveCandidate){.from = sq, .to = t, .promo = pr[j]},
|
||||
out, &n, max);
|
||||
}
|
||||
else
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = sq, .to = t, .promo = 0},
|
||||
out, &n, max);
|
||||
}
|
||||
// en passant
|
||||
if (pos->ep_square!=-1){
|
||||
int ep = pos->ep_square; if (abs(file_of(ep)-f)==1 && (ep == sq+dir+1 || ep==sq+dir-1)) add_move_if_legal(pos, sq, ep, 0, out, &n, max);
|
||||
}
|
||||
} break;
|
||||
case 'n': {
|
||||
for (int i=0;i<8;i++){
|
||||
int t = sq + knight_offsets[i]; if (!on_board(t)) continue; if (abs(file_of(t)-f)>2 || abs(rank_of(t)-r)>2) continue; if (!is_empty(pos, t) && same_color(p, pos->board[t])) continue; add_move_if_legal(pos, sq, t, 0, out, &n, max);
|
||||
}
|
||||
} break;
|
||||
case 'b': {
|
||||
for (int d=0; d<4; d++){
|
||||
int off = bishop_dirs[d]; int t = sq + off; while (on_board(t) && abs(file_of(t)-f)==abs(rank_of(t)-r)){
|
||||
if (!is_empty(pos, t)){ if (!same_color(p, pos->board[t])) add_move_if_legal(pos, sq, t, 0, out, &n, max); break; }
|
||||
add_move_if_legal(pos, sq, t, 0, out, &n, max); t += off;
|
||||
}
|
||||
// en passant
|
||||
if (pos->ep_square != -1)
|
||||
{
|
||||
int ep = pos->ep_square;
|
||||
if (abs(file_of(ep) - f) == 1 && (ep == sq + dir + 1 || ep == sq + dir - 1))
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = sq, .to = ep, .promo = 0}, out,
|
||||
&n, max);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
int t = sq + knight_offsets[i];
|
||||
if (!on_board(t))
|
||||
continue;
|
||||
if (abs(file_of(t) - f) > 2 || abs(rank_of(t) - r) > 2)
|
||||
continue;
|
||||
if (!is_empty(pos, t) && same_color(p, pos->board[t]))
|
||||
continue;
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = sq, .to = t, .promo = 0}, out, &n,
|
||||
max);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
{
|
||||
for (int d = 0; d < 4; d++)
|
||||
{
|
||||
int off = bishop_dirs[d];
|
||||
int t = sq + off;
|
||||
while (on_board(t) && abs(file_of(t) - f) == abs(rank_of(t) - r))
|
||||
{
|
||||
if (!is_empty(pos, t))
|
||||
{
|
||||
if (!same_color(p, pos->board[t]))
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = sq, .to = t, .promo = 0},
|
||||
out, &n, max);
|
||||
break;
|
||||
}
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = sq, .to = t, .promo = 0}, out,
|
||||
&n, max);
|
||||
t += off;
|
||||
}
|
||||
} break;
|
||||
case 'r': {
|
||||
for (int d=0; d<4; d++){
|
||||
int off = rook_dirs[d]; int t = sq + off; while (on_board(t) && (file_of(t)==f || rank_of(t)==r)){
|
||||
if (!is_empty(pos, t)){ if (!same_color(p, pos->board[t])) add_move_if_legal(pos, sq, t, 0, out, &n, max); break; }
|
||||
add_move_if_legal(pos, sq, t, 0, out, &n, max); t += off;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
{
|
||||
for (int d = 0; d < 4; d++)
|
||||
{
|
||||
int off = rook_dirs[d];
|
||||
int t = sq + off;
|
||||
while (on_board(t) && (file_of(t) == f || rank_of(t) == r))
|
||||
{
|
||||
if (!is_empty(pos, t))
|
||||
{
|
||||
if (!same_color(p, pos->board[t]))
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = sq, .to = t, .promo = 0},
|
||||
out, &n, max);
|
||||
break;
|
||||
}
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = sq, .to = t, .promo = 0}, out,
|
||||
&n, max);
|
||||
t += off;
|
||||
}
|
||||
} break;
|
||||
case 'q': {
|
||||
for (int d=0; d<4; d++){
|
||||
int off = bishop_dirs[d]; int t = sq + off; while (on_board(t) && abs(file_of(t)-f)==abs(rank_of(t)-r)){
|
||||
if (!is_empty(pos, t)){ if (!same_color(p, pos->board[t])) add_move_if_legal(pos, sq, t, 0, out, &n, max); break; }
|
||||
add_move_if_legal(pos, sq, t, 0, out, &n, max); t += off;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
{
|
||||
for (int d = 0; d < 4; d++)
|
||||
{
|
||||
int off = bishop_dirs[d];
|
||||
int t = sq + off;
|
||||
while (on_board(t) && abs(file_of(t) - f) == abs(rank_of(t) - r))
|
||||
{
|
||||
if (!is_empty(pos, t))
|
||||
{
|
||||
if (!same_color(p, pos->board[t]))
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = sq, .to = t, .promo = 0},
|
||||
out, &n, max);
|
||||
break;
|
||||
}
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = sq, .to = t, .promo = 0}, out,
|
||||
&n, max);
|
||||
t += off;
|
||||
}
|
||||
for (int d=0; d<4; d++){
|
||||
int off = rook_dirs[d]; int t = sq + off; while (on_board(t) && (file_of(t)==f || rank_of(t)==r)){
|
||||
if (!is_empty(pos, t)){ if (!same_color(p, pos->board[t])) add_move_if_legal(pos, sq, t, 0, out, &n, max); break; }
|
||||
add_move_if_legal(pos, sq, t, 0, out, &n, max); t += off;
|
||||
}
|
||||
for (int d = 0; d < 4; d++)
|
||||
{
|
||||
int off = rook_dirs[d];
|
||||
int t = sq + off;
|
||||
while (on_board(t) && (file_of(t) == f || rank_of(t) == r))
|
||||
{
|
||||
if (!is_empty(pos, t))
|
||||
{
|
||||
if (!same_color(p, pos->board[t]))
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = sq, .to = t, .promo = 0},
|
||||
out, &n, max);
|
||||
break;
|
||||
}
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = sq, .to = t, .promo = 0}, out,
|
||||
&n, max);
|
||||
t += off;
|
||||
}
|
||||
} break;
|
||||
case 'k': {
|
||||
for (int i=0;i<8;i++){
|
||||
int t = sq + king_dirs[i]; if (!on_board(t)) continue; if (abs(file_of(t)-f)>1||abs(rank_of(t)-r)>1) continue; if (!is_empty(pos, t) && same_color(p, pos->board[t])) continue; add_move_if_legal(pos, sq, t, 0, out, &n, max);
|
||||
}
|
||||
// castling
|
||||
if (white){
|
||||
if (pos->castle_wk && pos->board[5]=='.' && pos->board[6]=='.' && !chess_square_attacked(pos,4,false) && !chess_square_attacked(pos,5,false) && !chess_square_attacked(pos,6,false)) add_move_if_legal(pos, 4, 6, 0, out, &n, max);
|
||||
if (pos->castle_wq && pos->board[3]=='.' && pos->board[2]=='.' && pos->board[1]=='.' && !chess_square_attacked(pos,4,false) && !chess_square_attacked(pos,3,false) && !chess_square_attacked(pos,2,false)) add_move_if_legal(pos, 4, 2, 0, out, &n, max);
|
||||
} else {
|
||||
if (pos->castle_bk && pos->board[61]=='.' && pos->board[62]=='.' && !chess_square_attacked(pos,60,true) && !chess_square_attacked(pos,61,true) && !chess_square_attacked(pos,62,true)) add_move_if_legal(pos, 60, 62, 0, out, &n, max);
|
||||
if (pos->castle_bq && pos->board[59]=='.' && pos->board[58]=='.' && pos->board[57]=='.' && !chess_square_attacked(pos,60,true) && !chess_square_attacked(pos,59,true) && !chess_square_attacked(pos,58,true)) add_move_if_legal(pos, 60, 58, 0, out, &n, max);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'k':
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
int t = sq + king_dirs[i];
|
||||
if (!on_board(t))
|
||||
continue;
|
||||
if (abs(file_of(t) - f) > 1 || abs(rank_of(t) - r) > 1)
|
||||
continue;
|
||||
if (!is_empty(pos, t) && same_color(p, pos->board[t]))
|
||||
continue;
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = sq, .to = t, .promo = 0}, out, &n,
|
||||
max);
|
||||
}
|
||||
// castling
|
||||
if (white)
|
||||
{
|
||||
if (pos->castle_wk && pos->board[5] == '.' && pos->board[6] == '.' &&
|
||||
!chess_square_attacked(pos, 4, false) &&
|
||||
!chess_square_attacked(pos, 5, false) && !chess_square_attacked(pos, 6, false))
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = 4, .to = 6, .promo = 0}, out, &n,
|
||||
max);
|
||||
if (pos->castle_wq && pos->board[3] == '.' && pos->board[2] == '.' &&
|
||||
pos->board[1] == '.' && !chess_square_attacked(pos, 4, false) &&
|
||||
!chess_square_attacked(pos, 3, false) && !chess_square_attacked(pos, 2, false))
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = 4, .to = 2, .promo = 0}, out, &n,
|
||||
max);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pos->castle_bk && pos->board[61] == '.' && pos->board[62] == '.' &&
|
||||
!chess_square_attacked(pos, 60, true) &&
|
||||
!chess_square_attacked(pos, 61, true) && !chess_square_attacked(pos, 62, true))
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = 60, .to = 62, .promo = 0}, out,
|
||||
&n, max);
|
||||
if (pos->castle_bq && pos->board[59] == '.' && pos->board[58] == '.' &&
|
||||
pos->board[57] == '.' && !chess_square_attacked(pos, 60, true) &&
|
||||
!chess_square_attacked(pos, 59, true) && !chess_square_attacked(pos, 58, true))
|
||||
add_move_if_legal(pos, (MoveCandidate){.from = 60, .to = 58, .promo = 0}, out,
|
||||
&n, max);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
bool chess_make_move(Position *pos, Move *m){
|
||||
m->is_castle=false; m->is_enpassant=false;
|
||||
char p = pos->board[m->from]; char tgt = pos->board[m->to];
|
||||
if (p=='.') return false;
|
||||
bool chess_make_move(Position *pos, Move *m)
|
||||
{
|
||||
m->is_castle = false;
|
||||
m->is_enpassant = false;
|
||||
char p = pos->board[m->from];
|
||||
char tgt = pos->board[m->to];
|
||||
if (p == '.')
|
||||
return false;
|
||||
// handle special: en passant capture
|
||||
if (tolower(p)=='p' && m->to == pos->ep_square && file_of(m->to)!=file_of(m->from) && tgt=='.'){
|
||||
m->is_enpassant = true;
|
||||
int cap_sq = pos->white_to_move ? (m->to - 8) : (m->to + 8);
|
||||
m->captured = pos->board[cap_sq]; pos->board[cap_sq]='.';
|
||||
if (tolower(p) == 'p' && m->to == pos->ep_square && file_of(m->to) != file_of(m->from) &&
|
||||
tgt == '.')
|
||||
{
|
||||
m->is_enpassant = true;
|
||||
int cap_sq = pos->white_to_move ? (m->to - 8) : (m->to + 8);
|
||||
m->captured = pos->board[cap_sq];
|
||||
pos->board[cap_sq] = '.';
|
||||
}
|
||||
|
||||
// move piece
|
||||
pos->board[m->to] = p;
|
||||
pos->board[m->to] = p;
|
||||
pos->board[m->from] = '.';
|
||||
|
||||
// promotion
|
||||
if (tolower(p)=='p' && m->promo){ pos->board[m->to] = m->promo; }
|
||||
if (tolower(p) == 'p' && m->promo)
|
||||
{
|
||||
pos->board[m->to] = m->promo;
|
||||
}
|
||||
|
||||
// castling rook move
|
||||
if (tolower(p)=='k'){
|
||||
if (tolower(p) == 'k')
|
||||
{
|
||||
int from = m->from, to = m->to;
|
||||
if (from==4 && to==6){ pos->board[5]='R'; pos->board[7]='.'; m->is_castle=true; }
|
||||
else if (from==4 && to==2){ pos->board[3]='R'; pos->board[0]='.'; m->is_castle=true; }
|
||||
else if (from==60 && to==62){ pos->board[61]='r'; pos->board[63]='.'; m->is_castle=true; }
|
||||
else if (from==60 && to==58){ pos->board[59]='r'; pos->board[56]='.'; m->is_castle=true; }
|
||||
if (from == 4 && to == 6)
|
||||
{
|
||||
pos->board[5] = 'R';
|
||||
pos->board[7] = '.';
|
||||
m->is_castle = true;
|
||||
}
|
||||
else if (from == 4 && to == 2)
|
||||
{
|
||||
pos->board[3] = 'R';
|
||||
pos->board[0] = '.';
|
||||
m->is_castle = true;
|
||||
}
|
||||
else if (from == 60 && to == 62)
|
||||
{
|
||||
pos->board[61] = 'r';
|
||||
pos->board[63] = '.';
|
||||
m->is_castle = true;
|
||||
}
|
||||
else if (from == 60 && to == 58)
|
||||
{
|
||||
pos->board[59] = 'r';
|
||||
pos->board[56] = '.';
|
||||
m->is_castle = true;
|
||||
}
|
||||
}
|
||||
|
||||
// update castling rights
|
||||
if (m->from==0||m->to==0) pos->castle_wq=false;
|
||||
if (m->from==7||m->to==7) pos->castle_wk=false;
|
||||
if (m->from==56||m->to==56) pos->castle_bq=false;
|
||||
if (m->from==63||m->to==63) pos->castle_bk=false;
|
||||
if (tolower(p)=='k'){ if (is_white(p)) { pos->castle_wk=pos->castle_wq=false;} else {pos->castle_bk=pos->castle_bq=false;} }
|
||||
if (m->from == 0 || m->to == 0)
|
||||
pos->castle_wq = false;
|
||||
if (m->from == 7 || m->to == 7)
|
||||
pos->castle_wk = false;
|
||||
if (m->from == 56 || m->to == 56)
|
||||
pos->castle_bq = false;
|
||||
if (m->from == 63 || m->to == 63)
|
||||
pos->castle_bk = false;
|
||||
if (tolower(p) == 'k')
|
||||
{
|
||||
if (is_white(p))
|
||||
{
|
||||
pos->castle_wk = pos->castle_wq = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos->castle_bk = pos->castle_bq = false;
|
||||
}
|
||||
}
|
||||
|
||||
// update ep square
|
||||
pos->ep_square = -1;
|
||||
if (tolower(p)=='p'){
|
||||
if (tolower(p) == 'p')
|
||||
{
|
||||
int df = rank_of(m->to) - rank_of(m->from);
|
||||
if (df==2 || df==-2){ pos->ep_square = (m->from + m->to)/2; }
|
||||
if (df == 2 || df == -2)
|
||||
{
|
||||
pos->ep_square = (m->from + m->to) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
// halfmove clock
|
||||
if (tolower(p)=='p' || tgt!='.') pos->halfmove_clock = 0; else pos->halfmove_clock++;
|
||||
if (tolower(p) == 'p' || tgt != '.')
|
||||
pos->halfmove_clock = 0;
|
||||
else
|
||||
pos->halfmove_clock++;
|
||||
|
||||
// side to move
|
||||
pos->white_to_move = !pos->white_to_move; if (pos->white_to_move) pos->fullmove_number++;
|
||||
pos->white_to_move = !pos->white_to_move;
|
||||
if (pos->white_to_move)
|
||||
pos->fullmove_number++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void chess_unmake_move(Position *pos, const Move *m){
|
||||
pos->white_to_move = !pos->white_to_move; if (!pos->white_to_move) pos->fullmove_number--;
|
||||
void chess_unmake_move(Position *pos, const Move *m)
|
||||
{
|
||||
pos->white_to_move = !pos->white_to_move;
|
||||
if (!pos->white_to_move)
|
||||
pos->fullmove_number--;
|
||||
// restore halfmove/flags
|
||||
pos->ep_square = m->prev_ep;
|
||||
pos->castle_wk = m->prev_wk; pos->castle_wq = m->prev_wq; pos->castle_bk = m->prev_bk; pos->castle_bq = m->prev_bq;
|
||||
pos->ep_square = m->prev_ep;
|
||||
pos->castle_wk = m->prev_wk;
|
||||
pos->castle_wq = m->prev_wq;
|
||||
pos->castle_bk = m->prev_bk;
|
||||
pos->castle_bq = m->prev_bq;
|
||||
pos->halfmove_clock = m->prev_halfmove;
|
||||
|
||||
char p = m->moved;
|
||||
// undo promotions
|
||||
if (tolower(p)=='p' && m->promo){ p = is_white(p)? 'P':'p'; }
|
||||
if (tolower(p) == 'p' && m->promo)
|
||||
{
|
||||
p = is_white(p) ? 'P' : 'p';
|
||||
}
|
||||
|
||||
pos->board[m->from] = p;
|
||||
pos->board[m->to] = m->captured? m->captured : '.';
|
||||
if (m->is_enpassant){ int cap_sq = pos->white_to_move ? (m->to - 8) : (m->to + 8); pos->board[m->to]='.'; pos->board[cap_sq]= m->captured; }
|
||||
if (m->is_castle){
|
||||
if (m->from==4 && m->to==6){ pos->board[7]='R'; pos->board[5]='.'; }
|
||||
else if (m->from==4 && m->to==2){ pos->board[0]='R'; pos->board[3]='.'; }
|
||||
else if (m->from==60 && m->to==62){ pos->board[63]='r'; pos->board[61]='.'; }
|
||||
else if (m->from==60 && m->to==58){ pos->board[56]='r'; pos->board[59]='.'; }
|
||||
pos->board[m->to] = m->captured ? m->captured : '.';
|
||||
if (m->is_enpassant)
|
||||
{
|
||||
int cap_sq = pos->white_to_move ? (m->to - 8) : (m->to + 8);
|
||||
pos->board[m->to] = '.';
|
||||
pos->board[cap_sq] = m->captured;
|
||||
}
|
||||
if (m->is_castle)
|
||||
{
|
||||
if (m->from == 4 && m->to == 6)
|
||||
{
|
||||
pos->board[7] = 'R';
|
||||
pos->board[5] = '.';
|
||||
}
|
||||
else if (m->from == 4 && m->to == 2)
|
||||
{
|
||||
pos->board[0] = 'R';
|
||||
pos->board[3] = '.';
|
||||
}
|
||||
else if (m->from == 60 && m->to == 62)
|
||||
{
|
||||
pos->board[63] = 'r';
|
||||
pos->board[61] = '.';
|
||||
}
|
||||
else if (m->from == 60 && m->to == 58)
|
||||
{
|
||||
pos->board[56] = 'r';
|
||||
pos->board[59] = '.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sq_to_coord(int sq, int *file, int *rank){ if (file) *file=file_of(sq); if (rank) *rank=rank_of(sq); }
|
||||
int coord_to_sq(int file, int rank){ return rank*8 + file; }
|
||||
void sq_to_coord(int sq, int *file, int *rank)
|
||||
{
|
||||
if (file)
|
||||
*file = file_of(sq);
|
||||
if (rank)
|
||||
*rank = rank_of(sq);
|
||||
}
|
||||
int coord_to_sq(int file, int rank) { return rank * 8 + file; }
|
||||
|
||||
void move_to_uci(const Move *m, char buf[8]){
|
||||
int f1=file_of(m->from), r1=rank_of(m->from), f2=file_of(m->to), r2=rank_of(m->to);
|
||||
buf[0]='a'+f1; buf[1]='1'+r1; buf[2]='a'+f2; buf[3]='1'+r2; int i=4;
|
||||
if (m->promo){ buf[i++]=tolower(m->promo); }
|
||||
buf[i]='\0';
|
||||
void move_to_uci(const Move *m, char buf[8])
|
||||
{
|
||||
int f1 = file_of(m->from), r1 = rank_of(m->from), f2 = file_of(m->to), r2 = rank_of(m->to);
|
||||
buf[0] = (char)('a' + f1);
|
||||
buf[1] = (char)('1' + r1);
|
||||
buf[2] = (char)('a' + f2);
|
||||
buf[3] = (char)('1' + r2);
|
||||
int i = 4;
|
||||
if (m->promo)
|
||||
{
|
||||
buf[i++] = (char)tolower((unsigned char)m->promo);
|
||||
}
|
||||
buf[i] = '\0';
|
||||
}
|
||||
|
||||
bool parse_uci_move(const char *s, const Position *pos, Move *out){
|
||||
if (!s || strlen(s)<4) return false;
|
||||
int f1=s[0]-'a', r1=s[1]-'1', f2=s[2]-'a', r2=s[3]-'1'; if (f1<0||f1>7||f2<0||f2>7||r1<0||r1>7||r2<0||r2>7) return false;
|
||||
int from = r1*8+f1, to=r2*8+f2; char promo = s[4]? s[4]:0; if (promo) promo = pos->white_to_move? toupper((unsigned char)promo):tolower((unsigned char)promo);
|
||||
Move list[MAX_MOVES]; size_t n = chess_generate_legal_moves(pos, list, MAX_MOVES);
|
||||
for (size_t i=0;i<n;i++){ if (list[i].from==from && list[i].to==to){ *out = list[i]; out->promo = promo? promo : list[i].promo; return true; } }
|
||||
bool parse_uci_move(const char *s, const Position *pos, Move *out)
|
||||
{
|
||||
if (!s || strlen(s) < 4)
|
||||
return false;
|
||||
int f1 = s[0] - 'a', r1 = s[1] - '1', f2 = s[2] - 'a', r2 = s[3] - '1';
|
||||
if (f1 < 0 || f1 > 7 || f2 < 0 || f2 > 7 || r1 < 0 || r1 > 7 || r2 < 0 || r2 > 7)
|
||||
return false;
|
||||
int from = r1 * 8 + f1, to = r2 * 8 + f2;
|
||||
char promo = s[4] ? s[4] : 0;
|
||||
if (promo)
|
||||
promo = pos->white_to_move ? toupper((unsigned char)promo) : tolower((unsigned char)promo);
|
||||
Move list[MAX_MOVES];
|
||||
size_t n = chess_generate_legal_moves(pos, list, MAX_MOVES);
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
if (list[i].from == from && list[i].to == to)
|
||||
{
|
||||
*out = list[i];
|
||||
out->promo = promo ? promo : list[i].promo;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool chess_to_fen(const Position *pos, char *out, size_t outsz){
|
||||
char buf[256]; int idx=0;
|
||||
for (int r=7;r>=0;r--){
|
||||
int empty=0;
|
||||
for (int f=0;f<8;f++){
|
||||
char p = pos->board[r*8+f];
|
||||
if (p=='.') empty++; else { if (empty){ buf[idx++]= '0'+empty; empty=0; } buf[idx++]=p; }
|
||||
bool chess_to_fen(const Position *pos, char *out, size_t outsz)
|
||||
{
|
||||
char buf[256];
|
||||
int idx = 0;
|
||||
for (int r = 7; r >= 0; r--)
|
||||
{
|
||||
int empty = 0;
|
||||
for (int f = 0; f < 8; f++)
|
||||
{
|
||||
char p = pos->board[r * 8 + f];
|
||||
if (p == '.')
|
||||
empty++;
|
||||
else
|
||||
{
|
||||
if (empty)
|
||||
{
|
||||
buf[idx++] = (char)('0' + empty);
|
||||
empty = 0;
|
||||
}
|
||||
buf[idx++] = p;
|
||||
}
|
||||
}
|
||||
if (empty) buf[idx++]= '0'+empty;
|
||||
if (r) buf[idx++]='/';
|
||||
if (empty)
|
||||
buf[idx++] = (char)('0' + empty);
|
||||
if (r)
|
||||
buf[idx++] = '/';
|
||||
}
|
||||
buf[idx++]=' ';
|
||||
buf[idx++]= pos->white_to_move ? 'w':'b';
|
||||
buf[idx++]=' ';
|
||||
int start=idx;
|
||||
if (pos->castle_wk) buf[idx++]='K';
|
||||
if (pos->castle_wq) buf[idx++]='Q';
|
||||
if (pos->castle_bk) buf[idx++]='k';
|
||||
if (pos->castle_bq) buf[idx++]='q';
|
||||
if (idx==start) buf[idx++]='-';
|
||||
buf[idx++]=' ';
|
||||
if (pos->ep_square==-1){ buf[idx++]='-'; }
|
||||
else { int f=file_of(pos->ep_square), r=rank_of(pos->ep_square); buf[idx++]='a'+f; buf[idx++]='1'+r; }
|
||||
idx += snprintf(buf+idx, sizeof(buf)-idx, " %d %d", pos->halfmove_clock, pos->fullmove_number);
|
||||
buf[idx]='\0';
|
||||
buf[idx++] = ' ';
|
||||
buf[idx++] = pos->white_to_move ? 'w' : 'b';
|
||||
buf[idx++] = ' ';
|
||||
int start = idx;
|
||||
if (pos->castle_wk)
|
||||
buf[idx++] = 'K';
|
||||
if (pos->castle_wq)
|
||||
buf[idx++] = 'Q';
|
||||
if (pos->castle_bk)
|
||||
buf[idx++] = 'k';
|
||||
if (pos->castle_bq)
|
||||
buf[idx++] = 'q';
|
||||
if (idx == start)
|
||||
buf[idx++] = '-';
|
||||
buf[idx++] = ' ';
|
||||
if (pos->ep_square == -1)
|
||||
{
|
||||
buf[idx++] = '-';
|
||||
}
|
||||
else
|
||||
{
|
||||
int f = file_of(pos->ep_square), r = rank_of(pos->ep_square);
|
||||
buf[idx++] = (char)('a' + f);
|
||||
buf[idx++] = (char)('1' + r);
|
||||
}
|
||||
idx +=
|
||||
snprintf(buf + idx, sizeof(buf) - idx, " %d %d", pos->halfmove_clock, pos->fullmove_number);
|
||||
buf[idx] = '\0';
|
||||
snprintf(out, outsz, "%s", buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -7,25 +7,27 @@
|
||||
// Board is 64 chars, a1=0, b1=1, ..., h8=63
|
||||
// Pieces: 'P','N','B','R','Q','K' for white, lowercase for black, '.' empty
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
char board[64];
|
||||
bool white_to_move;
|
||||
bool castle_wk, castle_wq, castle_bk, castle_bq;
|
||||
int ep_square; // -1 if none
|
||||
int halfmove_clock;
|
||||
int fullmove_number;
|
||||
int ep_square; // -1 if none
|
||||
int halfmove_clock;
|
||||
int fullmove_number;
|
||||
} Position;
|
||||
|
||||
typedef struct {
|
||||
int from, to;
|
||||
char promo; // 0 or 'q','r','b','n' (lowercase for black)
|
||||
typedef struct
|
||||
{
|
||||
int from, to;
|
||||
char promo; // 0 or 'q','r','b','n' (lowercase for black)
|
||||
char captured; // piece captured or 0
|
||||
char moved; // piece moved
|
||||
bool is_castle;
|
||||
bool is_enpassant;
|
||||
int prev_ep;
|
||||
int prev_ep;
|
||||
bool prev_wk, prev_wq, prev_bk, prev_bq;
|
||||
int prev_halfmove;
|
||||
int prev_halfmove;
|
||||
} Move;
|
||||
|
||||
void chess_init_start(Position *pos);
|
||||
@ -33,8 +35,8 @@ void chess_copy(Position *dst, const Position *src);
|
||||
|
||||
// Move gen and make/unmake
|
||||
size_t chess_generate_legal_moves(const Position *pos, Move *out, size_t max);
|
||||
bool chess_make_move(Position *pos, Move *m);
|
||||
void chess_unmake_move(Position *pos, const Move *m);
|
||||
bool chess_make_move(Position *pos, Move *m);
|
||||
void chess_unmake_move(Position *pos, const Move *m);
|
||||
|
||||
// Utility
|
||||
bool chess_is_in_check(const Position *pos, bool white);
|
||||
@ -42,7 +44,7 @@ bool chess_square_attacked(const Position *pos, int sq, bool by_white);
|
||||
|
||||
// Conversions
|
||||
void sq_to_coord(int sq, int *file, int *rank);
|
||||
int coord_to_sq(int file, int rank);
|
||||
int coord_to_sq(int file, int rank);
|
||||
|
||||
// UCI strings like e2e4, with optional promotion char
|
||||
void move_to_uci(const Move *m, char buf[8]);
|
||||
|
||||
@ -1,139 +1,277 @@
|
||||
#include "engine.h"
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static bool spawn_process(const char *path, Engine *e){
|
||||
static void sleep_millis(unsigned int milliseconds)
|
||||
{
|
||||
struct timespec req = {
|
||||
.tv_sec = milliseconds / 1000,
|
||||
.tv_nsec = (long)(milliseconds % 1000) * 1000000L,
|
||||
};
|
||||
(void)nanosleep(&req, NULL);
|
||||
}
|
||||
|
||||
static bool spawn_process(const char *path, Engine *e)
|
||||
{
|
||||
int inpipe[2], outpipe[2];
|
||||
if (pipe(inpipe)<0 || pipe(outpipe)<0) return false;
|
||||
if (pipe(inpipe) < 0 || pipe(outpipe) < 0)
|
||||
return false;
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) return false;
|
||||
if (pid == 0){
|
||||
dup2(inpipe[0], STDIN_FILENO); // child stdin from inpipe[0]
|
||||
if (pid < 0)
|
||||
return false;
|
||||
if (pid == 0)
|
||||
{
|
||||
dup2(inpipe[0], STDIN_FILENO); // child stdin from inpipe[0]
|
||||
dup2(outpipe[1], STDOUT_FILENO); // child stdout to outpipe[1]
|
||||
dup2(outpipe[1], STDERR_FILENO);
|
||||
close(inpipe[0]); close(inpipe[1]); close(outpipe[0]); close(outpipe[1]);
|
||||
execlp(path, path, (char*)NULL);
|
||||
close(inpipe[0]);
|
||||
close(inpipe[1]);
|
||||
close(outpipe[0]);
|
||||
close(outpipe[1]);
|
||||
execlp(path, path, (char *)NULL);
|
||||
_exit(127);
|
||||
}
|
||||
// parent
|
||||
close(inpipe[0]); close(outpipe[1]);
|
||||
e->pid = pid; e->in_fd = inpipe[1]; e->out_fd = outpipe[0]; e->ready=false;
|
||||
close(inpipe[0]);
|
||||
close(outpipe[1]);
|
||||
e->pid = pid;
|
||||
e->in_fd = inpipe[1];
|
||||
e->out_fd = outpipe[0];
|
||||
e->ready = false;
|
||||
// make out_fd non-blocking for reads with polling
|
||||
int flags = fcntl(e->out_fd, F_GETFL, 0); fcntl(e->out_fd, F_SETFL, flags | O_NONBLOCK);
|
||||
int flags = fcntl(e->out_fd, F_GETFL, 0);
|
||||
fcntl(e->out_fd, F_SETFL, flags | O_NONBLOCK);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool try_start(Engine *e, const char *name){
|
||||
if (!spawn_process(name, e)) return false;
|
||||
static bool try_start(Engine *e, const char *name)
|
||||
{
|
||||
if (!spawn_process(name, e))
|
||||
return false;
|
||||
// send UCI init
|
||||
engine_cmd(e, "uci\n");
|
||||
char buf[4096]; int attempts=50; // ~5s total
|
||||
while (attempts--){
|
||||
usleep(100000);
|
||||
ssize_t n = read(e->out_fd, buf, sizeof(buf)-1);
|
||||
if (n>0){
|
||||
buf[n]='\0';
|
||||
if (strstr(buf, "uciok")) { e->ready=true; break; }
|
||||
char buf[4096];
|
||||
int attempts = 50; // ~5s total
|
||||
while (attempts--)
|
||||
{
|
||||
sleep_millis(100);
|
||||
ssize_t n = read(e->out_fd, buf, sizeof(buf) - 1);
|
||||
if (n > 0)
|
||||
{
|
||||
buf[n] = '\0';
|
||||
if (strstr(buf, "uciok"))
|
||||
{
|
||||
e->ready = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!e->ready){ engine_stop(e); return false; }
|
||||
if (!e->ready)
|
||||
{
|
||||
engine_stop(e);
|
||||
return false;
|
||||
}
|
||||
engine_cmd(e, "isready\n");
|
||||
attempts=50;
|
||||
while (attempts--){
|
||||
usleep(100000);
|
||||
ssize_t n = read(e->out_fd, buf, sizeof(buf)-1);
|
||||
if (n>0){ buf[n]='\0'; if (strstr(buf, "readyok")) break; }
|
||||
attempts = 50;
|
||||
while (attempts--)
|
||||
{
|
||||
sleep_millis(100);
|
||||
ssize_t n = read(e->out_fd, buf, sizeof(buf) - 1);
|
||||
if (n > 0)
|
||||
{
|
||||
buf[n] = '\0';
|
||||
if (strstr(buf, "readyok"))
|
||||
break;
|
||||
}
|
||||
}
|
||||
return e->ready;
|
||||
}
|
||||
|
||||
bool engine_start(Engine *e){
|
||||
memset(e, 0, sizeof(*e)); e->pid=-1; e->in_fd=-1; e->out_fd=-1; e->ready=false;
|
||||
if (try_start(e, "stockfish")) return true;
|
||||
if (try_start(e, "asmfish")) return true;
|
||||
bool engine_start(Engine *e)
|
||||
{
|
||||
memset(e, 0, sizeof(*e));
|
||||
e->pid = -1;
|
||||
e->in_fd = -1;
|
||||
e->out_fd = -1;
|
||||
e->ready = false;
|
||||
if (try_start(e, "stockfish"))
|
||||
return true;
|
||||
if (try_start(e, "asmfish"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void engine_stop(Engine *e){
|
||||
if (e->in_fd!=-1){ write(e->in_fd, "quit\n", 5); close(e->in_fd); }
|
||||
if (e->out_fd!=-1) close(e->out_fd);
|
||||
if (e->pid>0){ int status; waitpid(e->pid, &status, 0); }
|
||||
e->pid=-1; e->in_fd=e->out_fd=-1; e->ready=false;
|
||||
void engine_stop(Engine *e)
|
||||
{
|
||||
if (e->in_fd != -1)
|
||||
{
|
||||
write(e->in_fd, "quit\n", 5);
|
||||
close(e->in_fd);
|
||||
}
|
||||
if (e->out_fd != -1)
|
||||
close(e->out_fd);
|
||||
if (e->pid > 0)
|
||||
{
|
||||
int status;
|
||||
waitpid(e->pid, &status, 0);
|
||||
}
|
||||
e->pid = -1;
|
||||
e->in_fd = e->out_fd = -1;
|
||||
e->ready = false;
|
||||
}
|
||||
|
||||
bool engine_cmd(Engine *e, const char *cmd){
|
||||
if (e->in_fd==-1) return false;
|
||||
size_t len = strlen(cmd);
|
||||
ssize_t n = write(e->in_fd, cmd, len);
|
||||
if (n < 0) {
|
||||
bool engine_cmd(Engine *e, const char *cmd)
|
||||
{
|
||||
if (e->in_fd == -1)
|
||||
return false;
|
||||
size_t len = strlen(cmd);
|
||||
ssize_t n = write(e->in_fd, cmd, len);
|
||||
if (n < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return n == (ssize_t)len;
|
||||
}
|
||||
|
||||
static void position_to_uci(const Position *pos, char *out, size_t outsz){
|
||||
// Send starting position and moves list by comparing with startpos; for simplicity, use FEN always.
|
||||
char fen[256]; chess_to_fen(pos, fen, sizeof(fen));
|
||||
snprintf(out, outsz, "position fen %s\n", fen);
|
||||
static void position_to_uci(const Position *pos, char *out, size_t outsz)
|
||||
{
|
||||
// Send starting position and moves list by comparing with startpos; for simplicity, use FEN
|
||||
// always.
|
||||
char fen[256];
|
||||
chess_to_fen(pos, fen, sizeof(fen));
|
||||
(void)snprintf(out, outsz, "position fen %s\n", fen);
|
||||
}
|
||||
|
||||
size_t engine_get_top_moves(Engine *e, const Position *pos, EngineMove *out, size_t max){
|
||||
if (!e->ready) return 0;
|
||||
char cmd[512]; position_to_uci(pos, cmd, sizeof(cmd)); engine_cmd(e, cmd);
|
||||
size_t engine_get_top_moves(Engine *e, const Position *pos, EngineMove *out, size_t max)
|
||||
{
|
||||
if (!e->ready)
|
||||
return 0;
|
||||
char cmd[512];
|
||||
position_to_uci(pos, cmd, sizeof(cmd));
|
||||
engine_cmd(e, cmd);
|
||||
// ask multiPV up to max (cap at 5 as requested)
|
||||
size_t req = max; if (req>5) req=5; char go[128]; snprintf(go, sizeof(go), "setoption name MultiPV value %zu\n", req); engine_cmd(e, go);
|
||||
size_t req = max;
|
||||
if (req > 5)
|
||||
req = 5;
|
||||
char go[128];
|
||||
(void)snprintf(go, sizeof(go), "setoption name MultiPV value %zu\n", req);
|
||||
engine_cmd(e, go);
|
||||
engine_cmd(e, "go movetime 400\n");
|
||||
char buf[8192]; size_t count=0; int attempts=50;
|
||||
while (attempts--){
|
||||
usleep(100000);
|
||||
ssize_t n = read(e->out_fd, buf, sizeof(buf)-1);
|
||||
if (n<=0) continue;
|
||||
buf[n]='\0';
|
||||
char buf[8192];
|
||||
size_t count = 0;
|
||||
int attempts = 50;
|
||||
while (attempts--)
|
||||
{
|
||||
sleep_millis(100);
|
||||
ssize_t n = read(e->out_fd, buf, sizeof(buf) - 1);
|
||||
if (n <= 0)
|
||||
continue;
|
||||
buf[n] = '\0';
|
||||
char *line = strtok(buf, "\n");
|
||||
while (line){
|
||||
if (strncmp(line, "info ", 5)==0){
|
||||
while (line)
|
||||
{
|
||||
if (strncmp(line, "info ", 5) == 0)
|
||||
{
|
||||
// parse "info ... multipv X score cp Y ... pv <uci>"
|
||||
char *mpv = strstr(line, " multipv "); char *score = strstr(line, " score "); char *pv = strstr(line, " pv ");
|
||||
if (mpv && score && pv){
|
||||
int idx = atoi(mpv+9); if (idx>=1 && (size_t)idx<=req){
|
||||
int cp=0; if (strstr(score, "cp ")) cp = atoi(strstr(score, "cp ")+3);
|
||||
char mv[8]={0};
|
||||
sscanf(pv+4, "%7s", mv);
|
||||
size_t i = (size_t)idx-1; if (i<req){ out[i].score_cp = cp; snprintf(out[i].uci, sizeof(out[i].uci), "%s", mv); if (i+1>count) count=i+1; }
|
||||
char *mpv = strstr(line, " multipv ");
|
||||
char *score = strstr(line, " score ");
|
||||
char *pv = strstr(line, " pv ");
|
||||
if (mpv && score && pv)
|
||||
{
|
||||
char *idx_end = NULL;
|
||||
long idx_long = strtol(mpv + 9, &idx_end, 10);
|
||||
if (idx_end == mpv + 9 || idx_long < 1 || (size_t)idx_long > req)
|
||||
{
|
||||
line = strtok(NULL, "\n");
|
||||
continue;
|
||||
}
|
||||
size_t idx = (size_t)idx_long;
|
||||
if (idx >= 1 && idx <= req)
|
||||
{
|
||||
int cp = 0;
|
||||
char *cp_loc = strstr(score, "cp ");
|
||||
if (cp_loc)
|
||||
{
|
||||
char *cp_end = NULL;
|
||||
long cp_long = strtol(cp_loc + 3, &cp_end, 10);
|
||||
if (cp_end != cp_loc + 3 && cp_long >= INT_MIN && cp_long <= INT_MAX)
|
||||
{
|
||||
cp = (int)cp_long;
|
||||
}
|
||||
}
|
||||
char mv[8] = {0};
|
||||
if (sscanf(pv + 4, "%7s", mv) != 1)
|
||||
{
|
||||
line = strtok(NULL, "\n");
|
||||
continue;
|
||||
}
|
||||
size_t i = idx - 1;
|
||||
if (i < req)
|
||||
{
|
||||
out[i].score_cp = cp;
|
||||
(void)snprintf(out[i].uci, sizeof(out[i].uci), "%s", mv);
|
||||
if (i + 1 > count)
|
||||
count = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (strncmp(line, "bestmove ", 9)==0){ attempts=0; break; }
|
||||
}
|
||||
else if (strncmp(line, "bestmove ", 9) == 0)
|
||||
{
|
||||
attempts = 0;
|
||||
break;
|
||||
}
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
}
|
||||
// simple sort by score descending (best to worst), keeping empties at end
|
||||
for (size_t i=0;i<count;i++){
|
||||
for (size_t j=i+1;j<count;j++){
|
||||
if (out[j].score_cp > out[i].score_cp){ EngineMove tmp=out[i]; out[i]=out[j]; out[j]=tmp; }
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
for (size_t j = i + 1; j < count; j++)
|
||||
{
|
||||
if (out[j].score_cp > out[i].score_cp)
|
||||
{
|
||||
EngineMove tmp = out[i];
|
||||
out[i] = out[j];
|
||||
out[j] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool engine_get_best_move(Engine *e, const Position *pos, char out_uci[8]){
|
||||
if (!e->ready) return false;
|
||||
char cmd[512]; position_to_uci(pos, cmd, sizeof(cmd)); engine_cmd(e, cmd);
|
||||
bool engine_get_best_move(Engine *e, const Position *pos, char out_uci[8])
|
||||
{
|
||||
if (!e->ready)
|
||||
return false;
|
||||
char cmd[512];
|
||||
position_to_uci(pos, cmd, sizeof(cmd));
|
||||
engine_cmd(e, cmd);
|
||||
engine_cmd(e, "go movetime 300\n");
|
||||
char buf[4096]; int attempts=50;
|
||||
while (attempts--){
|
||||
usleep(100000);
|
||||
ssize_t n = read(e->out_fd, buf, sizeof(buf)-1);
|
||||
if (n<=0) continue;
|
||||
buf[n]='\0';
|
||||
char buf[4096];
|
||||
int attempts = 50;
|
||||
while (attempts--)
|
||||
{
|
||||
sleep_millis(100);
|
||||
ssize_t n = read(e->out_fd, buf, sizeof(buf) - 1);
|
||||
if (n <= 0)
|
||||
continue;
|
||||
buf[n] = '\0';
|
||||
char *line = strtok(buf, "\n");
|
||||
while (line){
|
||||
if (strncmp(line, "bestmove ", 9)==0){ sscanf(line+9, "%7s", out_uci); return true; }
|
||||
while (line)
|
||||
{
|
||||
if (strncmp(line, "bestmove ", 9) == 0)
|
||||
{
|
||||
return sscanf(line + 9, "%7s", out_uci) == 1;
|
||||
}
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,22 @@
|
||||
#ifndef ENGINE_H
|
||||
#define ENGINE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "chess.h"
|
||||
|
||||
typedef struct {
|
||||
int score_cp; // centipawns relative to side to move
|
||||
typedef struct
|
||||
{
|
||||
int score_cp; // centipawns relative to side to move
|
||||
char uci[8];
|
||||
} EngineMove;
|
||||
|
||||
typedef struct {
|
||||
int pid;
|
||||
int in_fd; // write to engine stdin
|
||||
int out_fd; // read from engine stdout
|
||||
typedef struct
|
||||
{
|
||||
int pid;
|
||||
int in_fd; // write to engine stdin
|
||||
int out_fd; // read from engine stdout
|
||||
bool ready;
|
||||
} Engine;
|
||||
|
||||
|
||||
@ -2,156 +2,229 @@
|
||||
#include <SDL2/SDL.h>
|
||||
#include <stdio.h>
|
||||
|
||||
const SDL_Color COLOR_LIGHT = { 238, 238, 210, 255 }; // light square (not pure white)
|
||||
const SDL_Color COLOR_DARK = { 118, 150, 86, 255 }; // dark square (not pure black)
|
||||
const SDL_Color COLOR_GRID = { 20, 20, 20, 255 }; // thick outline
|
||||
const SDL_Color COLOR_SEL = { 200, 50, 50, 200 }; // selection highlight
|
||||
const SDL_Color COLOR_TEXT = { 10, 10, 10, 255 };
|
||||
const SDL_Color COLOR_LIGHT = {238, 238, 210, 255}; // light square (not pure white)
|
||||
const SDL_Color COLOR_DARK = {118, 150, 86, 255}; // dark square (not pure black)
|
||||
const SDL_Color COLOR_GRID = {20, 20, 20, 255}; // thick outline
|
||||
const SDL_Color COLOR_SEL = {200, 50, 50, 200}; // selection highlight
|
||||
const SDL_Color COLOR_TEXT = {10, 10, 10, 255};
|
||||
|
||||
static void set_color(SDL_Renderer *r, SDL_Color c) {
|
||||
static void set_color(SDL_Renderer *r, SDL_Color c)
|
||||
{
|
||||
SDL_SetRenderDrawColor(r, c.r, c.g, c.b, c.a);
|
||||
}
|
||||
|
||||
bool gui_init(Gui *g, int w, int h, const char *title) {
|
||||
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
||||
fprintf(stderr, "SDL_Init error: %s\n", SDL_GetError());
|
||||
bool gui_init(Gui *g, int w, int h, const char *title)
|
||||
{
|
||||
if (SDL_Init(SDL_INIT_VIDEO) != 0)
|
||||
{
|
||||
(void)fprintf(stderr, "SDL_Init error: %s\n", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
g->window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w, h, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
|
||||
if (!g->window) {
|
||||
fprintf(stderr, "SDL_CreateWindow error: %s\n", SDL_GetError());
|
||||
g->window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w, h,
|
||||
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
|
||||
if (!g->window)
|
||||
{
|
||||
(void)fprintf(stderr, "SDL_CreateWindow error: %s\n", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
g->renderer = SDL_CreateRenderer(g->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||
if (!g->renderer) {
|
||||
fprintf(stderr, "SDL_CreateRenderer error: %s\n", SDL_GetError());
|
||||
g->renderer =
|
||||
SDL_CreateRenderer(g->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||
if (!g->renderer)
|
||||
{
|
||||
(void)fprintf(stderr, "SDL_CreateRenderer error: %s\n", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
g->win_w = w; g->win_h = h; g->flipped = false;
|
||||
g->win_w = w;
|
||||
g->win_h = h;
|
||||
g->flipped = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void gui_destroy(Gui *g) {
|
||||
if (g->renderer) SDL_DestroyRenderer(g->renderer);
|
||||
if (g->window) SDL_DestroyWindow(g->window);
|
||||
void gui_destroy(Gui *g)
|
||||
{
|
||||
if (g->renderer)
|
||||
SDL_DestroyRenderer(g->renderer);
|
||||
if (g->window)
|
||||
SDL_DestroyWindow(g->window);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void gui_set_flipped(Gui *g, bool flipped) { g->flipped = flipped; }
|
||||
|
||||
static void draw_rect(SDL_Renderer *r, int x, int y, int w, int h, SDL_Color c) {
|
||||
static void draw_rect(SDL_Renderer *r, int x, int y, int w, int h, SDL_Color c)
|
||||
{
|
||||
set_color(r, c);
|
||||
SDL_Rect rc = { x, y, w, h };
|
||||
SDL_Rect rc = {x, y, w, h};
|
||||
SDL_RenderFillRect(r, &rc);
|
||||
}
|
||||
|
||||
static void draw_outline(SDL_Renderer *r, int x, int y, int w, int h, int thickness, SDL_Color c) {
|
||||
static void draw_outline(SDL_Renderer *r, SDL_Rect bounds, int thickness, SDL_Color c)
|
||||
{
|
||||
set_color(r, c);
|
||||
for (int i=0;i<thickness;i++) {
|
||||
SDL_Rect rc = { x+i, y+i, w-2*i, h-2*i };
|
||||
for (int i = 0; i < thickness; i++)
|
||||
{
|
||||
SDL_Rect rc = {bounds.x + i, bounds.y + i, bounds.w - 2 * i, bounds.h - 2 * i};
|
||||
SDL_RenderDrawRect(r, &rc);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_piece_letter(SDL_Renderer *r, int x, int y, int size, char p) {
|
||||
static void draw_piece_letter(SDL_Renderer *r, SDL_Point origin, int cell_size, char piece)
|
||||
{
|
||||
// Minimal: draw a filled circle/square plus an initial letter approximated with rectangles.
|
||||
// To keep dependencies minimal, no TTF. Ensure contrast: white pieces light, black pieces dark.
|
||||
SDL_Color fill = (p >= 'A' && p <= 'Z') ? (SDL_Color){250, 250, 250, 255} : (SDL_Color){30, 30, 30, 255};
|
||||
SDL_Color glyph = (p >= 'A' && p <= 'Z') ? (SDL_Color){30, 30, 30, 255} : (SDL_Color){240, 240, 240, 255};
|
||||
SDL_Color fill = (piece >= 'A' && piece <= 'Z') ? (SDL_Color){250, 250, 250, 255}
|
||||
: (SDL_Color){30, 30, 30, 255};
|
||||
SDL_Color glyph = (piece >= 'A' && piece <= 'Z') ? (SDL_Color){30, 30, 30, 255}
|
||||
: (SDL_Color){240, 240, 240, 255};
|
||||
// Base disk
|
||||
draw_rect(r, x+size*0.15, y+size*0.15, (int)(size*0.7), (int)(size*0.7), fill);
|
||||
int disk_offset = (int)(cell_size * 0.15f);
|
||||
int disk_size = (int)(cell_size * 0.7f);
|
||||
draw_rect(r, origin.x + disk_offset, origin.y + disk_offset, disk_size, disk_size, fill);
|
||||
// Glyph: draw a simple letter-like mark
|
||||
set_color(r, glyph);
|
||||
// vertical bar
|
||||
SDL_Rect bar1 = { x + size/2 - size/16, y + size/3, size/8, size/3 };
|
||||
SDL_Rect bar1 = {origin.x + cell_size / 2 - cell_size / 16, origin.y + cell_size / 3,
|
||||
cell_size / 8, cell_size / 3};
|
||||
SDL_RenderFillRect(r, &bar1);
|
||||
// top bar
|
||||
SDL_Rect bar2 = { x + size/3, y + size/3 - size/10, size/3, size/10 };
|
||||
SDL_Rect bar2 = {origin.x + cell_size / 3, origin.y + cell_size / 3 - cell_size / 10,
|
||||
cell_size / 3, cell_size / 10};
|
||||
SDL_RenderFillRect(r, &bar2);
|
||||
}
|
||||
|
||||
void gui_draw(Gui *g, const char board[64], const GuiSelection *sel, const char *status_line) {
|
||||
void gui_draw(Gui *g, const char board[64], const GuiSelection *sel, const char *status_line)
|
||||
{
|
||||
SDL_GetWindowSize(g->window, &g->win_w, &g->win_h);
|
||||
|
||||
set_color(g->renderer, (SDL_Color){ 35, 35, 35, 255 });
|
||||
set_color(g->renderer, (SDL_Color){35, 35, 35, 255});
|
||||
SDL_RenderClear(g->renderer);
|
||||
|
||||
int size = (g->win_w < g->win_h ? g->win_w : g->win_h) - 40; // margins
|
||||
if (size < 200) size = 200;
|
||||
if (size < 200)
|
||||
size = 200;
|
||||
int cell = size / 8;
|
||||
size = cell * 8;
|
||||
int ox = (g->win_w - size)/2;
|
||||
int oy = (g->win_h - size)/2;
|
||||
size = cell * 8;
|
||||
int ox = (g->win_w - size) / 2;
|
||||
int oy = (g->win_h - size) / 2;
|
||||
|
||||
// Board outline (thick)
|
||||
draw_outline(g->renderer, ox-6, oy-6, size+12, size+12, 6, COLOR_GRID);
|
||||
SDL_Rect board_bounds = {ox - 6, oy - 6, size + 12, size + 12};
|
||||
draw_outline(g->renderer, board_bounds, 6, COLOR_GRID);
|
||||
|
||||
// Squares
|
||||
for (int r=0;r<8;r++) {
|
||||
for (int f=0;f<8;f++) {
|
||||
int idx = g->flipped ? (63 - (r*8+f)) : (r*8+f);
|
||||
SDL_Color c = ((r+f)&1) ? COLOR_DARK : COLOR_LIGHT;
|
||||
draw_rect(g->renderer, ox + f*cell, oy + r*cell, cell, cell, c);
|
||||
for (int r = 0; r < 8; r++)
|
||||
{
|
||||
for (int f = 0; f < 8; f++)
|
||||
{
|
||||
int idx = g->flipped ? (63 - (r * 8 + f)) : (r * 8 + f);
|
||||
SDL_Color c = ((r + f) & 1) ? COLOR_DARK : COLOR_LIGHT;
|
||||
int cell_x = ox + f * cell;
|
||||
int cell_y = oy + r * cell;
|
||||
draw_rect(g->renderer, cell_x, cell_y, cell, cell, c);
|
||||
|
||||
char p = board[idx];
|
||||
if (p != '.' && p != '\0') {
|
||||
draw_piece_letter(g->renderer, ox + f*cell, oy + r*cell, cell, p);
|
||||
char piece = board[idx];
|
||||
if (piece != '.' && piece != '\0')
|
||||
{
|
||||
SDL_Point origin = {cell_x, cell_y};
|
||||
draw_piece_letter(g->renderer, origin, cell, piece);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Selection overlay
|
||||
if (sel && sel->clicked && sel->from_sq >= 0) {
|
||||
int s = sel->from_sq;
|
||||
int rr = g->flipped ? 7 - (s/8) : (s/8);
|
||||
int ff = g->flipped ? 7 - (s%8) : (s%8);
|
||||
draw_outline(g->renderer, ox + ff*cell+2, oy + rr*cell+2, cell-4, cell-4, 3, COLOR_SEL);
|
||||
if (sel && sel->clicked && sel->from_sq >= 0)
|
||||
{
|
||||
int s = sel->from_sq;
|
||||
int rr = g->flipped ? 7 - (s / 8) : (s / 8);
|
||||
int ff = g->flipped ? 7 - (s % 8) : (s % 8);
|
||||
SDL_Rect sel_bounds = {ox + ff * cell + 2, oy + rr * cell + 2, cell - 4, cell - 4};
|
||||
draw_outline(g->renderer, sel_bounds, 3, COLOR_SEL);
|
||||
}
|
||||
|
||||
// Status strip
|
||||
draw_outline(g->renderer, 10, g->win_h - 40, g->win_w - 20, 30, 2, COLOR_GRID);
|
||||
SDL_Rect status_bounds = {10, g->win_h - 40, g->win_w - 20, 30};
|
||||
draw_outline(g->renderer, status_bounds, 2, COLOR_GRID);
|
||||
// Without TTF, we can't render text; draw a minimal indicator bar to signal state.
|
||||
// If status_line indicates success/failure, alter color.
|
||||
SDL_Color bar = { 80, 120, 200, 255 };
|
||||
if (status_line && status_line[0]) {
|
||||
if (SDL_strstr(status_line, "Correct")) bar = (SDL_Color){80, 200, 120, 255};
|
||||
else if (SDL_strstr(status_line, "Wrong")) bar = (SDL_Color){200, 80, 80, 255};
|
||||
SDL_Color bar = {80, 120, 200, 255};
|
||||
if (status_line && status_line[0])
|
||||
{
|
||||
if (SDL_strstr(status_line, "Correct"))
|
||||
bar = (SDL_Color){80, 200, 120, 255};
|
||||
else if (SDL_strstr(status_line, "Wrong"))
|
||||
bar = (SDL_Color){200, 80, 80, 255};
|
||||
}
|
||||
draw_rect(g->renderer, 12, g->win_h - 38, g->win_w - 24, 26, bar);
|
||||
|
||||
SDL_RenderPresent(g->renderer);
|
||||
}
|
||||
|
||||
int gui_coord_to_sq(Gui *g, int x, int y) {
|
||||
int w, h; SDL_GetWindowSize(g->window, &w, &h);
|
||||
int size = (w < h ? w : h) - 40; if (size < 200) size = 200;
|
||||
int cell = size / 8; size = cell * 8;
|
||||
int ox = (w - size)/2; int oy = (h - size)/2;
|
||||
if (x < ox || y < oy || x >= ox+size || y >= oy+size) return -1;
|
||||
int f = (x - ox) / cell;
|
||||
int r = (y - oy) / cell;
|
||||
int sq = r*8 + f;
|
||||
if (g->flipped) sq = 63 - sq;
|
||||
int gui_coord_to_sq(Gui *g, int x, int y)
|
||||
{
|
||||
int w, h;
|
||||
SDL_GetWindowSize(g->window, &w, &h);
|
||||
int size = (w < h ? w : h) - 40;
|
||||
if (size < 200)
|
||||
size = 200;
|
||||
int cell = size / 8;
|
||||
size = cell * 8;
|
||||
int ox = (w - size) / 2;
|
||||
int oy = (h - size) / 2;
|
||||
if (x < ox || y < oy || x >= ox + size || y >= oy + size)
|
||||
return -1;
|
||||
int f = (x - ox) / cell;
|
||||
int r = (y - oy) / cell;
|
||||
int sq = r * 8 + f;
|
||||
if (g->flipped)
|
||||
sq = 63 - sq;
|
||||
return sq;
|
||||
}
|
||||
|
||||
bool gui_poll_move(Gui *g, GuiSelection *sel, bool *quit_requested, int *key_out) {
|
||||
bool gui_poll_move(Gui *g, GuiSelection *sel, bool *quit_requested, int *key_out)
|
||||
{
|
||||
SDL_Event e;
|
||||
bool updated = false;
|
||||
if (key_out) *key_out = 0;
|
||||
while (SDL_PollEvent(&e)) {
|
||||
if (e.type == SDL_QUIT) { if (quit_requested) *quit_requested = true; }
|
||||
else if (e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
|
||||
bool updated = false;
|
||||
if (key_out)
|
||||
*key_out = 0;
|
||||
while (SDL_PollEvent(&e))
|
||||
{
|
||||
if (e.type == SDL_QUIT)
|
||||
{
|
||||
if (quit_requested)
|
||||
*quit_requested = true;
|
||||
}
|
||||
else if (e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
|
||||
{
|
||||
updated = true;
|
||||
} else if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT) {
|
||||
}
|
||||
else if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT)
|
||||
{
|
||||
int sq = gui_coord_to_sq(g, e.button.x, e.button.y);
|
||||
if (sq >= 0) {
|
||||
if (!sel->clicked) { sel->from_sq = sq; sel->to_sq = -1; sel->clicked = true; }
|
||||
else { sel->to_sq = sq; updated = true; }
|
||||
if (sq >= 0)
|
||||
{
|
||||
if (!sel->clicked)
|
||||
{
|
||||
sel->from_sq = sq;
|
||||
sel->to_sq = -1;
|
||||
sel->clicked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
sel->to_sq = sq;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
} else if (e.type == SDL_KEYDOWN) {
|
||||
if (e.key.keysym.sym == SDLK_ESCAPE) { sel->clicked = false; sel->from_sq = sel->to_sq = -1; updated = true; }
|
||||
if (key_out) *key_out = e.key.keysym.sym;
|
||||
}
|
||||
else if (e.type == SDL_KEYDOWN)
|
||||
{
|
||||
if (e.key.keysym.sym == SDLK_ESCAPE)
|
||||
{
|
||||
sel->clicked = false;
|
||||
sel->from_sq = sel->to_sq = -1;
|
||||
updated = true;
|
||||
}
|
||||
if (key_out)
|
||||
*key_out = e.key.keysym.sym;
|
||||
}
|
||||
}
|
||||
return updated;
|
||||
|
||||
@ -1,20 +1,22 @@
|
||||
#ifndef GUI_H
|
||||
#define GUI_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
SDL_Window *window;
|
||||
typedef struct
|
||||
{
|
||||
SDL_Window *window;
|
||||
SDL_Renderer *renderer;
|
||||
int win_w, win_h;
|
||||
bool flipped; // true if black at bottom
|
||||
int win_w, win_h;
|
||||
bool flipped; // true if black at bottom
|
||||
} Gui;
|
||||
|
||||
typedef struct {
|
||||
int from_sq; // 0..63 or -1
|
||||
int to_sq; // 0..63 or -1
|
||||
char promo; // 'q','r','b','n' or 0
|
||||
typedef struct
|
||||
{
|
||||
int from_sq; // 0..63 or -1
|
||||
int to_sq; // 0..63 or -1
|
||||
char promo; // 'q','r','b','n' or 0
|
||||
bool clicked;
|
||||
} GuiSelection;
|
||||
|
||||
@ -24,7 +26,7 @@ void gui_set_flipped(Gui *g, bool flipped);
|
||||
void gui_draw(Gui *g, const char board[64], const GuiSelection *sel, const char *status_line);
|
||||
// Returns true if something changed. If a key was pressed, key_out receives SDL_Keycode else 0.
|
||||
bool gui_poll_move(Gui *g, GuiSelection *sel, bool *quit_requested, int *key_out);
|
||||
int gui_coord_to_sq(Gui *g, int x, int y);
|
||||
int gui_coord_to_sq(Gui *g, int x, int y);
|
||||
|
||||
// colors
|
||||
extern const SDL_Color COLOR_LIGHT;
|
||||
|
||||
@ -1,193 +1,283 @@
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "gui.h"
|
||||
#include "chess.h"
|
||||
#include "engine.h"
|
||||
#include "gui.h"
|
||||
#include "mistakes.h"
|
||||
|
||||
typedef struct {
|
||||
Position pos;
|
||||
Engine engine;
|
||||
Gui gui;
|
||||
MistakeList mistakes;
|
||||
bool replay_mode; // allow revisiting mistakes
|
||||
size_t replay_index;
|
||||
typedef struct
|
||||
{
|
||||
Position pos;
|
||||
Engine engine;
|
||||
Gui gui;
|
||||
MistakeList mistakes;
|
||||
bool replay_mode; // allow revisiting mistakes
|
||||
size_t replay_index;
|
||||
} App;
|
||||
|
||||
static void append_uci(char *line, size_t sz, const char *mv){
|
||||
if (line[0]) strncat(line, " ", sz-1);
|
||||
strncat(line, mv, sz-1);
|
||||
static void append_uci(char *line, size_t sz, const char *mv)
|
||||
{
|
||||
if (line[0])
|
||||
strncat(line, " ", sz - 1);
|
||||
strncat(line, mv, sz - 1);
|
||||
}
|
||||
|
||||
static void position_push_move_text(Position *pos, const Move *m, char *line, size_t lsz){
|
||||
(void)pos;
|
||||
char u[8]; move_to_uci(m, u);
|
||||
append_uci(line, lsz, u);
|
||||
static void position_push_move_text(Position *pos, const Move *m, char *line, size_t lsz)
|
||||
{
|
||||
(void)pos;
|
||||
char u[8];
|
||||
move_to_uci(m, u);
|
||||
append_uci(line, lsz, u);
|
||||
}
|
||||
|
||||
static void collect_all_legal_uci(const Position *pos, char list[][8], size_t *n, size_t max){
|
||||
Move mv[MAX_MOVES]; size_t k = chess_generate_legal_moves(pos, mv, MAX_MOVES);
|
||||
*n = 0;
|
||||
for (size_t i=0;i<k && *n<max;i++){ char u[8]; move_to_uci(&mv[i], u); strncpy(list[(*n)++], u, 8); }
|
||||
static void collect_all_legal_uci(const Position *pos, char list[][8], size_t *n, size_t max)
|
||||
{
|
||||
Move mv[MAX_MOVES];
|
||||
size_t k = chess_generate_legal_moves(pos, mv, MAX_MOVES);
|
||||
*n = 0;
|
||||
for (size_t i = 0; i < k && *n < max; i++)
|
||||
{
|
||||
char u[8];
|
||||
move_to_uci(&mv[i], u);
|
||||
strncpy(list[(*n)++], u, 8);
|
||||
}
|
||||
}
|
||||
|
||||
static bool uci_in_list(const char *u, char list[][8], size_t n){
|
||||
for (size_t i=0;i<n;i++){
|
||||
if (strncmp(u, list[i], 8)==0) return true;
|
||||
}
|
||||
return false;
|
||||
static bool uci_in_list(const char *u, char list[][8], size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
if (strncmp(u, list[i], 8) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char *mistake_file_path(){ return "mistakes.txt"; }
|
||||
static const char *mistake_file_path(void) { return "mistakes.txt"; }
|
||||
|
||||
int main(){
|
||||
srand((unsigned)time(NULL));
|
||||
int main(void)
|
||||
{
|
||||
srand((unsigned)time(NULL));
|
||||
|
||||
App app; memset(&app, 0, sizeof(app));
|
||||
mistakes_init(&app.mistakes);
|
||||
mistakes_load(&app.mistakes, mistake_file_path());
|
||||
App app;
|
||||
memset(&app, 0, sizeof(app));
|
||||
mistakes_init(&app.mistakes);
|
||||
mistakes_load(&app.mistakes, mistake_file_path());
|
||||
|
||||
// Avoid SIGPIPE crashes when engine pipe closes
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
// Avoid SIGPIPE crashes when engine pipe closes
|
||||
(void)signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
if (!gui_init(&app.gui, 720, 760, "Opening Learner")){
|
||||
fprintf(stderr, "GUI init failed.\n");
|
||||
return 1;
|
||||
}
|
||||
if (!gui_init(&app.gui, 720, 760, "Opening Learner"))
|
||||
{
|
||||
(void)fprintf(stderr, "GUI init failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!engine_start(&app.engine)){
|
||||
fprintf(stderr, "Error: Neither stockfish nor asmfish found locally. Please install one.\n");
|
||||
gui_destroy(&app.gui);
|
||||
return 1;
|
||||
}
|
||||
if (!engine_start(&app.engine))
|
||||
{
|
||||
(void)fprintf(stderr,
|
||||
"Error: Neither stockfish nor asmfish found locally. Please install one.\n");
|
||||
gui_destroy(&app.gui);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialize position
|
||||
chess_init_start(&app.pos);
|
||||
// Initialize position
|
||||
chess_init_start(&app.pos);
|
||||
|
||||
// Randomly pick side
|
||||
bool player_is_white = rand()%2==0;
|
||||
gui_set_flipped(&app.gui, !player_is_white);
|
||||
// Randomly pick side
|
||||
bool player_is_white = rand() % 2 == 0;
|
||||
gui_set_flipped(&app.gui, !player_is_white);
|
||||
|
||||
// gameplay state
|
||||
char status[128] = "";
|
||||
GuiSelection sel = { .from_sq=-1,.to_sq=-1,.promo=0,.clicked=false };
|
||||
char line_uci[512] = ""; // history of moves uci
|
||||
// gameplay state
|
||||
char status[128] = "";
|
||||
GuiSelection sel = {.from_sq = -1, .to_sq = -1, .promo = 0, .clicked = false};
|
||||
char line_uci[512] = ""; // history of moves uci
|
||||
|
||||
// If white, player moves first
|
||||
bool awaiting_player = player_is_white;
|
||||
char expected_player_move[8] = ""; // best move suggested by engine for player
|
||||
bool quit=false;
|
||||
// If white, player moves first
|
||||
bool awaiting_player = player_is_white;
|
||||
char expected_player_move[8] = ""; // best move suggested by engine for player
|
||||
bool quit = false;
|
||||
|
||||
while (!quit){
|
||||
// Show board
|
||||
gui_draw(&app.gui, app.pos.board, &sel, status);
|
||||
while (!quit)
|
||||
{
|
||||
// Show board
|
||||
gui_draw(&app.gui, app.pos.board, &sel, status);
|
||||
|
||||
// Engine to act when it's engine turn
|
||||
if (!awaiting_player){
|
||||
// 2. Ask engine for proposed responses (5)
|
||||
EngineMove props[5]; size_t n = engine_get_top_moves(&app.engine, &app.pos, props, 5);
|
||||
// 3. Sort is done in engine; also collect legal ones not proposed
|
||||
char legal[256][8]; size_t lcnt=0; collect_all_legal_uci(&app.pos, legal, &lcnt, 256);
|
||||
// 4. pick response with decreasing probability
|
||||
size_t total = n;
|
||||
// add non-proposed at the end with minimal priority
|
||||
char pool[300][8]; int weights[300]; size_t pcnt=0;
|
||||
for (size_t i=0;i<n;i++){ strncpy(pool[pcnt], props[i].uci, 8); weights[pcnt++] = (int)(n - i); }
|
||||
for (size_t i=0;i<lcnt;i++){
|
||||
if (!uci_in_list(legal[i], pool, pcnt)) {
|
||||
memcpy(pool[pcnt], legal[i], 8);
|
||||
pool[pcnt][7] = '\0';
|
||||
weights[pcnt++] = 1; total++;
|
||||
}
|
||||
}
|
||||
// weighted pick
|
||||
int wsum=0; for (size_t i=0;i<pcnt;i++) wsum += weights[i];
|
||||
int r = (wsum>0)? (rand()%wsum) : 0;
|
||||
size_t pick=0; for (size_t i=0;i<pcnt;i++){ if (r < weights[i]) { pick=i; break; } r -= weights[i]; }
|
||||
// 5. play response
|
||||
Move m; if (!parse_uci_move(pool[pick], &app.pos, &m)){ // fallback: best
|
||||
if (n>0 && parse_uci_move(props[0].uci, &app.pos, &m)) pick=0; else { snprintf(status, sizeof(status), "No engine move"); quit=true; continue; }
|
||||
}
|
||||
chess_make_move(&app.pos, &m);
|
||||
position_push_move_text(&app.pos, &m, line_uci, sizeof(line_uci));
|
||||
awaiting_player = true;
|
||||
// 6. Ask engine for optimal response from player
|
||||
engine_get_best_move(&app.engine, &app.pos, expected_player_move);
|
||||
snprintf(status, sizeof(status), "Your turn");
|
||||
continue;
|
||||
}
|
||||
// Engine to act when it's engine turn
|
||||
if (!awaiting_player)
|
||||
{
|
||||
// 2. Ask engine for proposed responses (5)
|
||||
EngineMove props[5];
|
||||
size_t n = engine_get_top_moves(&app.engine, &app.pos, props, 5);
|
||||
// 3. Sort is done in engine; also collect legal ones not proposed
|
||||
char legal[256][8];
|
||||
size_t lcnt = 0;
|
||||
collect_all_legal_uci(&app.pos, legal, &lcnt, 256);
|
||||
// 4. pick response with decreasing probability
|
||||
// add non-proposed at the end with minimal priority
|
||||
char pool[300][8];
|
||||
int weights[300];
|
||||
size_t pcnt = 0;
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
strncpy(pool[pcnt], props[i].uci, 8);
|
||||
weights[pcnt++] = (int)(n - i);
|
||||
}
|
||||
for (size_t i = 0; i < lcnt; i++)
|
||||
{
|
||||
if (!uci_in_list(legal[i], pool, pcnt))
|
||||
{
|
||||
memcpy(pool[pcnt], legal[i], 8);
|
||||
pool[pcnt][7] = '\0';
|
||||
weights[pcnt++] = 1;
|
||||
}
|
||||
}
|
||||
// weighted pick
|
||||
int wsum = 0;
|
||||
for (size_t i = 0; i < pcnt; i++)
|
||||
wsum += weights[i];
|
||||
int r = (wsum > 0) ? (rand() % wsum) : 0;
|
||||
size_t pick = 0;
|
||||
for (size_t i = 0; i < pcnt; i++)
|
||||
{
|
||||
if (r < weights[i])
|
||||
{
|
||||
pick = i;
|
||||
break;
|
||||
}
|
||||
r -= weights[i];
|
||||
}
|
||||
// 5. play response
|
||||
Move m;
|
||||
if (!parse_uci_move(pool[pick], &app.pos, &m))
|
||||
{ // fallback: best
|
||||
if (!(n > 0 && parse_uci_move(props[0].uci, &app.pos, &m)))
|
||||
{
|
||||
(void)snprintf(status, sizeof(status), "No engine move");
|
||||
quit = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
chess_make_move(&app.pos, &m);
|
||||
position_push_move_text(&app.pos, &m, line_uci, sizeof(line_uci));
|
||||
awaiting_player = true;
|
||||
// 6. Ask engine for optimal response from player
|
||||
engine_get_best_move(&app.engine, &app.pos, expected_player_move);
|
||||
(void)snprintf(status, sizeof(status), "Your turn");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Player move input
|
||||
int key=0;
|
||||
bool updated = gui_poll_move(&app.gui, &sel, &quit, &key);
|
||||
if (quit) break;
|
||||
if (key=='m' || key=='M'){
|
||||
// enter simple replay: load a mistake line and restart from it to the position where best move is required
|
||||
if (app.mistakes.count>0){
|
||||
if (app.replay_index >= app.mistakes.count) app.replay_index = 0;
|
||||
Mistake *mk = &app.mistakes.items[app.replay_index++];
|
||||
// reset and play the line moves to reach the mistake position
|
||||
chess_init_start(&app.pos);
|
||||
char tmp[512]; snprintf(tmp, sizeof(tmp), "%s", mk->line);
|
||||
char *tok = strtok(tmp, " ");
|
||||
while (tok){ Move m; if (parse_uci_move(tok, &app.pos, &m)) chess_make_move(&app.pos, &m); tok = strtok(NULL, " "); }
|
||||
snprintf(status, sizeof(status), "Practice: best is %s", mk->best_move);
|
||||
strncpy(expected_player_move, mk->best_move, sizeof(expected_player_move));
|
||||
awaiting_player = true;
|
||||
gui_set_flipped(&app.gui, !app.pos.white_to_move); // if it's black to move, flip so black bottom
|
||||
}
|
||||
}
|
||||
if (updated && sel.clicked && sel.to_sq>=0){
|
||||
Move list[MAX_MOVES]; size_t n = chess_generate_legal_moves(&app.pos, list, MAX_MOVES);
|
||||
bool moved=false; Move chosen={0};
|
||||
for (size_t i=0;i<n;i++){
|
||||
if (list[i].from==sel.from_sq && list[i].to==sel.to_sq){ chosen = list[i]; moved=true; break; }
|
||||
}
|
||||
sel.clicked=false; sel.from_sq=sel.to_sq=-1; sel.promo=0;
|
||||
if (!moved) continue;
|
||||
// Player move input
|
||||
int key = 0;
|
||||
bool updated = gui_poll_move(&app.gui, &sel, &quit, &key);
|
||||
if (quit)
|
||||
break;
|
||||
if (key == 'm' || key == 'M')
|
||||
{
|
||||
// enter simple replay: load a mistake line and restart from it to the position where
|
||||
// best move is required
|
||||
if (app.mistakes.count > 0)
|
||||
{
|
||||
if (app.replay_index >= app.mistakes.count)
|
||||
app.replay_index = 0;
|
||||
Mistake *mk = &app.mistakes.items[app.replay_index++];
|
||||
// reset and play the line moves to reach the mistake position
|
||||
chess_init_start(&app.pos);
|
||||
char tmp[512];
|
||||
(void)snprintf(tmp, sizeof(tmp), "%s", mk->line);
|
||||
char *tok = strtok(tmp, " ");
|
||||
while (tok)
|
||||
{
|
||||
Move m;
|
||||
if (parse_uci_move(tok, &app.pos, &m))
|
||||
chess_make_move(&app.pos, &m);
|
||||
tok = strtok(NULL, " ");
|
||||
}
|
||||
(void)snprintf(status, sizeof(status), "Practice: best is %s", mk->best_move);
|
||||
strncpy(expected_player_move, mk->best_move, sizeof(expected_player_move));
|
||||
awaiting_player = true;
|
||||
gui_set_flipped(
|
||||
&app.gui,
|
||||
!app.pos.white_to_move); // if it's black to move, flip so black bottom
|
||||
}
|
||||
}
|
||||
if (updated && sel.clicked && sel.to_sq >= 0)
|
||||
{
|
||||
Move list[MAX_MOVES];
|
||||
size_t n = chess_generate_legal_moves(&app.pos, list, MAX_MOVES);
|
||||
bool moved = false;
|
||||
Move chosen = {0};
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
if (list[i].from == sel.from_sq && list[i].to == sel.to_sq)
|
||||
{
|
||||
chosen = list[i];
|
||||
moved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sel.clicked = false;
|
||||
sel.from_sq = sel.to_sq = -1;
|
||||
sel.promo = 0;
|
||||
if (!moved)
|
||||
continue;
|
||||
|
||||
// 7. Compare with expected move
|
||||
char uci[8]; move_to_uci(&chosen, uci);
|
||||
bool correct = (expected_player_move[0] && strncmp(uci, expected_player_move, 8)==0);
|
||||
if (correct){
|
||||
chess_make_move(&app.pos, &chosen);
|
||||
position_push_move_text(&app.pos, &chosen, line_uci, sizeof(line_uci));
|
||||
snprintf(status, sizeof(status), "Correct");
|
||||
awaiting_player = false; // engine move next
|
||||
} else {
|
||||
// log mistake: 1) save all moves that lead to mistake, 2) allow revisit later, 3) reset position
|
||||
char fen[256]; chess_to_fen(&app.pos, fen, sizeof(fen));
|
||||
mistakes_add(&app.mistakes, fen, expected_player_move, line_uci);
|
||||
mistakes_save(&app.mistakes, mistake_file_path());
|
||||
snprintf(status, sizeof(status), "Wrong, best was %s", expected_player_move);
|
||||
// redo best move to show
|
||||
Move best; if (parse_uci_move(expected_player_move, &app.pos, &best)){
|
||||
chess_make_move(&app.pos, &best);
|
||||
position_push_move_text(&app.pos, &best, line_uci, sizeof(line_uci));
|
||||
}
|
||||
gui_draw(&app.gui, app.pos.board, &sel, status);
|
||||
SDL_Delay(600);
|
||||
// reset to start
|
||||
chess_init_start(&app.pos);
|
||||
line_uci[0]='\0';
|
||||
// randomize side again
|
||||
player_is_white = rand()%2==0; gui_set_flipped(&app.gui, !player_is_white);
|
||||
awaiting_player = player_is_white;
|
||||
expected_player_move[0]='\0';
|
||||
}
|
||||
}
|
||||
// 7. Compare with expected move
|
||||
char uci[8];
|
||||
move_to_uci(&chosen, uci);
|
||||
bool correct = (expected_player_move[0] && strncmp(uci, expected_player_move, 8) == 0);
|
||||
if (correct)
|
||||
{
|
||||
chess_make_move(&app.pos, &chosen);
|
||||
position_push_move_text(&app.pos, &chosen, line_uci, sizeof(line_uci));
|
||||
(void)snprintf(status, sizeof(status), "Correct");
|
||||
awaiting_player = false; // engine move next
|
||||
}
|
||||
else
|
||||
{
|
||||
// log mistake: 1) save all moves that lead to mistake, 2) allow revisit later, 3)
|
||||
// reset position
|
||||
char fen[256];
|
||||
chess_to_fen(&app.pos, fen, sizeof(fen));
|
||||
MistakeEntry entry = {
|
||||
.fen = fen,
|
||||
.best_move = expected_player_move,
|
||||
.line = line_uci,
|
||||
};
|
||||
mistakes_add(&app.mistakes, &entry);
|
||||
(void)mistakes_save(&app.mistakes, mistake_file_path());
|
||||
(void)snprintf(status, sizeof(status), "Wrong, best was %s", expected_player_move);
|
||||
// redo best move to show
|
||||
Move best;
|
||||
if (parse_uci_move(expected_player_move, &app.pos, &best))
|
||||
{
|
||||
chess_make_move(&app.pos, &best);
|
||||
position_push_move_text(&app.pos, &best, line_uci, sizeof(line_uci));
|
||||
}
|
||||
gui_draw(&app.gui, app.pos.board, &sel, status);
|
||||
SDL_Delay(600);
|
||||
// reset to start
|
||||
chess_init_start(&app.pos);
|
||||
line_uci[0] = '\0';
|
||||
// randomize side again
|
||||
player_is_white = rand() % 2 == 0;
|
||||
gui_set_flipped(&app.gui, !player_is_white);
|
||||
awaiting_player = player_is_white;
|
||||
expected_player_move[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Delay(10);
|
||||
}
|
||||
SDL_Delay(10);
|
||||
}
|
||||
|
||||
mistakes_save(&app.mistakes, mistake_file_path());
|
||||
gui_destroy(&app.gui);
|
||||
engine_stop(&app.engine);
|
||||
mistakes_free(&app.mistakes);
|
||||
return 0;
|
||||
mistakes_save(&app.mistakes, mistake_file_path());
|
||||
gui_destroy(&app.gui);
|
||||
engine_stop(&app.engine);
|
||||
mistakes_free(&app.mistakes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,65 +1,102 @@
|
||||
#include "mistakes.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void mistakes_init(MistakeList *ml) {
|
||||
ml->items = NULL; ml->count = 0; ml->cap = 0;
|
||||
void mistakes_init(MistakeList *ml)
|
||||
{
|
||||
ml->items = NULL;
|
||||
ml->count = 0;
|
||||
ml->cap = 0;
|
||||
}
|
||||
|
||||
void mistakes_free(MistakeList *ml) {
|
||||
free(ml->items); ml->items = NULL; ml->count = ml->cap = 0;
|
||||
void mistakes_free(MistakeList *ml)
|
||||
{
|
||||
free(ml->items);
|
||||
ml->items = NULL;
|
||||
ml->count = ml->cap = 0;
|
||||
}
|
||||
|
||||
static void ensure_cap(MistakeList *ml, size_t need) {
|
||||
if (need <= ml->cap) return;
|
||||
size_t ncap = ml->cap ? ml->cap*2 : 16;
|
||||
while (ncap < need) ncap *= 2;
|
||||
static void ensure_cap(MistakeList *ml, size_t need)
|
||||
{
|
||||
if (need <= ml->cap)
|
||||
return;
|
||||
size_t ncap = ml->cap ? ml->cap * 2 : 16;
|
||||
while (ncap < need)
|
||||
ncap *= 2;
|
||||
Mistake *ni = realloc(ml->items, ncap * sizeof(Mistake));
|
||||
if (!ni) return; // OOM silently ignored
|
||||
ml->items = ni; ml->cap = ncap;
|
||||
if (!ni)
|
||||
return; // OOM silently ignored
|
||||
ml->items = ni;
|
||||
ml->cap = ncap;
|
||||
}
|
||||
|
||||
void mistakes_add(MistakeList *ml, const char *fen, const char *best_move, const char *line) {
|
||||
ensure_cap(ml, ml->count+1);
|
||||
void mistakes_add(MistakeList *ml, const MistakeEntry *entry)
|
||||
{
|
||||
ensure_cap(ml, ml->count + 1);
|
||||
Mistake *m = &ml->items[ml->count++];
|
||||
snprintf(m->fen, sizeof(m->fen), "%s", fen);
|
||||
snprintf(m->best_move, sizeof(m->best_move), "%s", best_move);
|
||||
snprintf(m->line, sizeof(m->line), "%s", line);
|
||||
(void)snprintf(m->fen, sizeof(m->fen), "%s", entry->fen);
|
||||
(void)snprintf(m->best_move, sizeof(m->best_move), "%s", entry->best_move);
|
||||
(void)snprintf(m->line, sizeof(m->line), "%s", entry->line);
|
||||
}
|
||||
|
||||
bool mistakes_save(const MistakeList *ml, const char *path) {
|
||||
bool mistakes_save(const MistakeList *ml, const char *path)
|
||||
{
|
||||
FILE *f = fopen(path, "w");
|
||||
if (!f) return false;
|
||||
for (size_t i=0;i<ml->count;i++) {
|
||||
if (!f)
|
||||
return false;
|
||||
for (size_t i = 0; i < ml->count; i++)
|
||||
{
|
||||
const Mistake *m = &ml->items[i];
|
||||
fprintf(f, "FEN:%s\nBEST:%s\nLINE:%s\n.\n", m->fen, m->best_move, m->line);
|
||||
(void)fprintf(f, "FEN:%s\nBEST:%s\nLINE:%s\n.\n", m->fen, m->best_move, m->line);
|
||||
}
|
||||
fclose(f); return true;
|
||||
(void)fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mistakes_load(MistakeList *ml, const char *path) {
|
||||
bool mistakes_load(MistakeList *ml, const char *path)
|
||||
{
|
||||
FILE *f = fopen(path, "r");
|
||||
if (!f) return false;
|
||||
char buf[1024]; char fen[128] = ""; char best[16] = ""; char line[512] = "";
|
||||
while (fgets(buf, sizeof(buf), f)) {
|
||||
if (strncmp(buf, "FEN:", 4) == 0) {
|
||||
if (!f)
|
||||
return false;
|
||||
char buf[1024];
|
||||
char fen[128] = "";
|
||||
char best[16] = "";
|
||||
char line[512] = "";
|
||||
while (fgets(buf, sizeof(buf), f))
|
||||
{
|
||||
if (strncmp(buf, "FEN:", 4) == 0)
|
||||
{
|
||||
// copy up to 127 chars, strip newline
|
||||
size_t l = strnlen(buf+4, sizeof(fen)-1);
|
||||
memcpy(fen, buf+4, l); fen[l]='\0';
|
||||
if (l && fen[l-1]=='\n') fen[l-1]='\0';
|
||||
} else if (strncmp(buf, "BEST:", 5) == 0) {
|
||||
size_t l = strnlen(buf+5, sizeof(best)-1);
|
||||
memcpy(best, buf+5, l); best[l]='\0';
|
||||
if (l && best[l-1]=='\n') best[l-1]='\0';
|
||||
} else if (strncmp(buf, "LINE:", 5) == 0) {
|
||||
size_t l = strnlen(buf+5, sizeof(line)-1);
|
||||
memcpy(line, buf+5, l); line[l]='\0';
|
||||
if (l && line[l-1]=='\n') line[l-1]='\0';
|
||||
} else if (buf[0]=='.') {
|
||||
mistakes_add(ml, fen, best, line);
|
||||
fen[0]=best[0]=line[0]='\0';
|
||||
size_t l = strcspn(buf + 4, "\n");
|
||||
if (l >= sizeof(fen))
|
||||
l = sizeof(fen) - 1;
|
||||
memcpy(fen, buf + 4, l);
|
||||
fen[l] = '\0';
|
||||
}
|
||||
else if (strncmp(buf, "BEST:", 5) == 0)
|
||||
{
|
||||
size_t l = strcspn(buf + 5, "\n");
|
||||
if (l >= sizeof(best))
|
||||
l = sizeof(best) - 1;
|
||||
memcpy(best, buf + 5, l);
|
||||
best[l] = '\0';
|
||||
}
|
||||
else if (strncmp(buf, "LINE:", 5) == 0)
|
||||
{
|
||||
size_t l = strcspn(buf + 5, "\n");
|
||||
if (l >= sizeof(line))
|
||||
l = sizeof(line) - 1;
|
||||
memcpy(line, buf + 5, l);
|
||||
line[l] = '\0';
|
||||
}
|
||||
else if (buf[0] == '.')
|
||||
{
|
||||
MistakeEntry entry = {.fen = fen, .best_move = best, .line = line};
|
||||
mistakes_add(ml, &entry);
|
||||
fen[0] = best[0] = line[0] = '\0';
|
||||
}
|
||||
}
|
||||
fclose(f); return true;
|
||||
(void)fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1,27 +1,36 @@
|
||||
#ifndef MISTAKES_H
|
||||
#define MISTAKES_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// A lightweight mistake store in memory + file persistence.
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
char fen[128];
|
||||
char best_move[8];
|
||||
// PGN-like ply list in UCI for context
|
||||
char line[512];
|
||||
} Mistake;
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
Mistake *items;
|
||||
size_t count;
|
||||
size_t cap;
|
||||
size_t count;
|
||||
size_t cap;
|
||||
} MistakeList;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *fen;
|
||||
const char *best_move;
|
||||
const char *line;
|
||||
} MistakeEntry;
|
||||
|
||||
void mistakes_init(MistakeList *ml);
|
||||
void mistakes_free(MistakeList *ml);
|
||||
void mistakes_add(MistakeList *ml, const char *fen, const char *best_move, const char *line);
|
||||
void mistakes_add(MistakeList *ml, const MistakeEntry *entry);
|
||||
bool mistakes_save(const MistakeList *ml, const char *path);
|
||||
bool mistakes_load(MistakeList *ml, const char *path);
|
||||
|
||||
|
||||
206
C/scrapeWebsite/scrape.c
Normal file
206
C/scrapeWebsite/scrape.c
Normal file
@ -0,0 +1,206 @@
|
||||
#include <curl/curl.h>
|
||||
#include <libxml/HTMLparser.h>
|
||||
#include <libxml/uri.h>
|
||||
#include <libxml/xpath.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Structure to store downloaded data
|
||||
struct MemoryStruct
|
||||
{
|
||||
char *memory;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
// Write callback function for curl
|
||||
static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
size_t realsize = size * nmemb;
|
||||
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
|
||||
|
||||
char *ptr = realloc(mem->memory, mem->size + realsize + 1);
|
||||
if (ptr == NULL)
|
||||
{
|
||||
printf("Not enough memory!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mem->memory = ptr;
|
||||
memcpy(&(mem->memory[mem->size]), contents, realsize);
|
||||
mem->size += realsize;
|
||||
mem->memory[mem->size] = 0;
|
||||
|
||||
return realsize;
|
||||
}
|
||||
|
||||
// Initialize the curl request for the URL
|
||||
CURL *init_curl_request(const char *url, struct MemoryStruct *chunk)
|
||||
{
|
||||
CURL *curl = curl_easy_init();
|
||||
if (curl)
|
||||
{
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)chunk);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
}
|
||||
return curl;
|
||||
}
|
||||
|
||||
// Download the image file
|
||||
int download_image(const char *url, const char *image_name)
|
||||
{
|
||||
if (access(image_name, F_OK) != -1)
|
||||
{
|
||||
printf("Image %s already exists, skipping download.\n", image_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
CURL *curl = curl_easy_init();
|
||||
if (curl)
|
||||
{
|
||||
FILE *fp = fopen(image_name, "wb");
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
fclose(fp);
|
||||
curl_easy_cleanup(curl);
|
||||
return res == CURLE_OK ? 1 : 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Parse HTML and find the XPath expression
|
||||
xmlChar *get_xpath_value(htmlDocPtr doc, const char *xpathExpr)
|
||||
{
|
||||
xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
|
||||
xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression((xmlChar *)xpathExpr, xpathCtx);
|
||||
xmlChar *result = NULL;
|
||||
|
||||
if (xpathObj && !xmlXPathNodeSetIsEmpty(xpathObj->nodesetval))
|
||||
{
|
||||
result = xmlNodeListGetString(doc, xpathObj->nodesetval->nodeTab[0]->xmlChildrenNode, 1);
|
||||
}
|
||||
xmlXPathFreeObject(xpathObj);
|
||||
xmlXPathFreeContext(xpathCtx);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Extract the image URL and download it
|
||||
void extract_and_download_image(htmlDocPtr doc, const char *url)
|
||||
{
|
||||
xmlChar *image_url = get_xpath_value(doc, "//*[@id='cc-comic']/@src");
|
||||
if (image_url)
|
||||
{
|
||||
printf("Found image URL: %s\n", image_url);
|
||||
char *image_name = strrchr((char *)image_url, '/');
|
||||
if (image_name)
|
||||
{
|
||||
image_name++; // Skip the '/'
|
||||
download_image((char *)image_url, image_name);
|
||||
}
|
||||
xmlFree(image_url);
|
||||
}
|
||||
}
|
||||
|
||||
// Find and return the next button URL
|
||||
char *find_next_button_url(htmlDocPtr doc)
|
||||
{
|
||||
xmlChar *next_url = get_xpath_value(doc, "//a[contains(@class,'cc-next')]/@href");
|
||||
if (next_url)
|
||||
{
|
||||
char *url_copy = strdup((char *)next_url);
|
||||
xmlFree(next_url);
|
||||
return url_copy;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Reset chunk memory size before performing curl request
|
||||
void reset_chunk_size(struct MemoryStruct *chunk) { chunk->size = 0; }
|
||||
|
||||
// Perform curl request and return result
|
||||
CURLcode perform_curl_request(CURL *curl)
|
||||
{
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK)
|
||||
{
|
||||
printf("curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Parse the HTML document from the chunk memory
|
||||
htmlDocPtr parse_html_from_chunk(struct MemoryStruct *chunk, const char *url)
|
||||
{
|
||||
return htmlReadMemory(chunk->memory, chunk->size, url, NULL,
|
||||
HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
|
||||
}
|
||||
|
||||
// Handle processing of the current HTML document
|
||||
int process_html_document(htmlDocPtr doc, const char **url)
|
||||
{
|
||||
extract_and_download_image(doc, *url);
|
||||
char *next_url = find_next_button_url(doc);
|
||||
|
||||
if (next_url)
|
||||
{
|
||||
printf("Next URL: %s\n", next_url);
|
||||
*url = next_url;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Reached the end of images.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up resources used during processing
|
||||
void clean_up(CURL *curl, struct MemoryStruct *chunk)
|
||||
{
|
||||
curl_easy_cleanup(curl);
|
||||
free(chunk->memory);
|
||||
}
|
||||
|
||||
// Process the images and follow the next button
|
||||
void process_images(const char *url)
|
||||
{
|
||||
struct MemoryStruct chunk = {malloc(1), 0};
|
||||
CURL *curl = init_curl_request(url, &chunk);
|
||||
CURLcode res;
|
||||
|
||||
if (curl)
|
||||
{
|
||||
do
|
||||
{
|
||||
reset_chunk_size(&chunk);
|
||||
res = perform_curl_request(curl);
|
||||
|
||||
if (res != CURLE_OK)
|
||||
break;
|
||||
|
||||
htmlDocPtr doc = parse_html_from_chunk(&chunk, url);
|
||||
if (!doc)
|
||||
break;
|
||||
|
||||
if (!process_html_document(doc, &url))
|
||||
break;
|
||||
|
||||
xmlFreeDoc(doc);
|
||||
} while (res == CURLE_OK);
|
||||
|
||||
clean_up(curl, &chunk);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
const char *url = "..."; // Replace with your actual URL
|
||||
process_images(url);
|
||||
printf("All images processed.\n");
|
||||
return 0;
|
||||
}
|
||||
10
C/tests/generatingPolishLettersOnWindowsTerminal.c
Normal file
10
C/tests/generatingPolishLettersOnWindowsTerminal.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("Henlo\n");
|
||||
sleep(20);
|
||||
return 0;
|
||||
}
|
||||
@ -1,68 +1,74 @@
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
|
||||
static int interrupted;
|
||||
static int interrupted;
|
||||
static struct lws_context *context;
|
||||
|
||||
// Callback for WebSocket communication
|
||||
static int callback_function(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len) {
|
||||
switch (reason) {
|
||||
case LWS_CALLBACK_CLIENT_ESTABLISHED:
|
||||
printf("WebSocket connection established\n");
|
||||
break;
|
||||
static int callback_function(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
||||
void *in, size_t len)
|
||||
{
|
||||
switch (reason)
|
||||
{
|
||||
case LWS_CALLBACK_CLIENT_ESTABLISHED:
|
||||
printf("WebSocket connection established\n");
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_RECEIVE:
|
||||
printf("Received data: %s\n", (char *)in);
|
||||
break;
|
||||
case LWS_CALLBACK_CLIENT_RECEIVE:
|
||||
printf("Received data: %s\n", (char *)in);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_WRITEABLE: {
|
||||
const char *msg = "Hello, WebSocket server!";
|
||||
unsigned char buf[LWS_PRE + 512];
|
||||
size_t msg_len = strlen(msg);
|
||||
memcpy(&buf[LWS_PRE], msg, msg_len);
|
||||
lws_write(wsi, &buf[LWS_PRE], msg_len, LWS_WRITE_TEXT);
|
||||
break;
|
||||
}
|
||||
case LWS_CALLBACK_CLIENT_WRITEABLE:
|
||||
{
|
||||
const char *msg = "Hello, WebSocket server!";
|
||||
unsigned char buf[LWS_PRE + 512];
|
||||
size_t msg_len = strlen(msg);
|
||||
memcpy(&buf[LWS_PRE], msg, msg_len);
|
||||
lws_write(wsi, &buf[LWS_PRE], msg_len, LWS_WRITE_TEXT);
|
||||
break;
|
||||
}
|
||||
|
||||
case LWS_CALLBACK_CLOSED:
|
||||
printf("WebSocket connection closed\n");
|
||||
interrupted = 1;
|
||||
break;
|
||||
case LWS_CALLBACK_CLOSED:
|
||||
printf("WebSocket connection closed\n");
|
||||
interrupted = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Signal handler for clean exit
|
||||
static void sigint_handler(int sig) {
|
||||
static void sigint_handler(int sig)
|
||||
{
|
||||
interrupted = 1;
|
||||
lws_cancel_service(context);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int main(void)
|
||||
{
|
||||
struct lws_protocols protocols[] = {
|
||||
{
|
||||
"ws-protocol", // Protocol name
|
||||
"ws-protocol", // Protocol name
|
||||
callback_function, // Callback function
|
||||
0, // Per-session data size
|
||||
0, // Per-session data size
|
||||
0,
|
||||
},
|
||||
{NULL, NULL, 0, 0} // End of list
|
||||
{NULL, NULL, 0, 0} // End of list
|
||||
};
|
||||
|
||||
struct lws_client_connect_info ccinfo = {0};
|
||||
struct lws_context_creation_info info = {0};
|
||||
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||
info.protocols = protocols;
|
||||
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
struct lws_client_connect_info ccinfo = {0};
|
||||
struct lws_context_creation_info info = {0};
|
||||
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||
info.protocols = protocols;
|
||||
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
|
||||
// Create WebSocket context
|
||||
context = lws_create_context(&info);
|
||||
if (!context) {
|
||||
if (!context)
|
||||
{
|
||||
fprintf(stderr, "Failed to create context\n");
|
||||
return -1;
|
||||
}
|
||||
@ -70,25 +76,27 @@ int main(void) {
|
||||
signal(SIGINT, sigint_handler);
|
||||
|
||||
// Configure connection details
|
||||
ccinfo.context = context;
|
||||
ccinfo.address = "echo.websocket.org"; // WebSocket server address
|
||||
ccinfo.port = 443; // Port (for WSS use 443)
|
||||
ccinfo.path = "/"; // Path on the server
|
||||
ccinfo.host = lws_canonical_hostname(context);
|
||||
ccinfo.origin = "origin";
|
||||
ccinfo.protocol = protocols[0].name;
|
||||
ccinfo.ssl_connection = LCCSCF_USE_SSL; // Use SSL for secure WebSocket
|
||||
ccinfo.context = context;
|
||||
ccinfo.address = "echo.websocket.org"; // WebSocket server address
|
||||
ccinfo.port = 443; // Port (for WSS use 443)
|
||||
ccinfo.path = "/"; // Path on the server
|
||||
ccinfo.host = lws_canonical_hostname(context);
|
||||
ccinfo.origin = "origin";
|
||||
ccinfo.protocol = protocols[0].name;
|
||||
ccinfo.ssl_connection = LCCSCF_USE_SSL; // Use SSL for secure WebSocket
|
||||
|
||||
// Initiate the WebSocket connection
|
||||
struct lws *wsi = lws_client_connect_via_info(&ccinfo);
|
||||
if (!wsi) {
|
||||
if (!wsi)
|
||||
{
|
||||
fprintf(stderr, "Failed to initiate WebSocket connection\n");
|
||||
lws_context_destroy(context);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Event loop
|
||||
while (!interrupted) {
|
||||
while (!interrupted)
|
||||
{
|
||||
lws_service(context, 1000);
|
||||
}
|
||||
|
||||
@ -97,4 +105,3 @@ int main(void) {
|
||||
printf("Exiting...\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user