diff --git a/C/1dvelocitysimulator/main.c b/C/1dvelocitysimulator/main.c new file mode 100644 index 0000000..0a9ee10 --- /dev/null +++ b/C/1dvelocitysimulator/main.c @@ -0,0 +1,168 @@ +#include +#include +#include +#include +#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; +} diff --git a/C/fps/main.c b/C/fps/main.c index 21f875e..f67e153 100644 --- a/C/fps/main.c +++ b/C/fps/main.c @@ -4,62 +4,84 @@ // - Targets: red cubes move toward you; shoot them before they reach you // - Game over when a target reaches you; final score shown; press R to restart -#include +#include +#include #include +#include +#include +#include +#include +#include #include #include #include #include -#include #include -#include -#include +#include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif // -------- Math helpers -------- -typedef struct { float x, y, z; } vec3; +typedef struct +{ + float x, y, z; +} vec3; -static inline vec3 v3(float x, float y, float z) { vec3 v = {x,y,z}; return v; } -static inline vec3 v3_add(vec3 a, vec3 b) { return v3(a.x+b.x, a.y+b.y, a.z+b.z); } -static inline vec3 v3_sub(vec3 a, vec3 b) { return v3(a.x-b.x, a.y-b.y, a.z-b.z); } -static inline vec3 v3_scale(vec3 a, float s) { return v3(a.x*s, a.y*s, a.z*s); } -static inline float v3_dot(vec3 a, vec3 b) { return a.x*b.x + a.y*b.y + a.z*b.z; } -static inline vec3 v3_cross(vec3 a, vec3 b) { - return v3(a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x); +static inline vec3 v3(float x, float y, float z) +{ + vec3 v = {x, y, z}; + return v; } -static inline float v3_len(vec3 a) { return sqrtf(v3_dot(a,a)); } -static inline vec3 v3_norm(vec3 a) { - float l = v3_len(a); - return l > 1e-6f ? v3_scale(a, 1.0f/l) : v3(0,0,0); +static inline vec3 v3_add(vec3 a, vec3 b) { return v3(a.x + b.x, a.y + b.y, a.z + b.z); } +static inline vec3 v3_sub(vec3 a, vec3 b) { return v3(a.x - b.x, a.y - b.y, a.z - b.z); } +static inline vec3 v3_scale(vec3 a, float s) { return v3(a.x * s, a.y * s, a.z * s); } +static inline float v3_dot(vec3 a, vec3 b) { return (a.x * b.x) + (a.y * b.y) + (a.z * b.z); } +static inline vec3 v3_cross(vec3 a, vec3 b) +{ + return v3((a.y * b.z) - (a.z * b.y), (a.z * b.x) - (a.x * b.z), (a.x * b.y) - (a.y * b.x)); +} +static inline float v3_len(vec3 a) { return sqrtf(v3_dot(a, a)); } +static inline vec3 v3_norm(vec3 a) +{ + float l = v3_len(a); + return l > 1e-6F ? v3_scale(a, 1.0F / l) : v3(0, 0, 0); } // -------- Global state -------- -static int g_win_w = 1280, g_win_h = 720; -static bool g_keys[256] = {0}; +static int g_win_w = 1280, g_win_h = 720; +static bool g_keys[256] = {0}; static bool g_captured_mouse = true; -static int g_last_mouse_x = -1, g_last_mouse_y = -1; +static int g_last_mouse_x = -1, g_last_mouse_y = -1; static bool g_ignore_next_passive = false; // avoid warp feedback loops -static vec3 g_cam_pos = {0.0f, 1.6f, 5.0f}; -static float g_yaw_deg = -90.0f; // facing -Z initially -static float g_pitch_deg = 0.0f; +static vec3 g_cam_pos = {0.0F, 1.6F, 5.0F}; +static float g_yaw_deg = -90.0F; // facing -Z initially +static float g_pitch_deg = 0.0F; -static float g_move_speed = 4.0f; // m/s -static float g_sprint_mul = 1.8f; -static float g_mouse_sens = 0.12f; // deg per pixel +static float g_move_speed = 4.0F; // m/s +static float g_sprint_mul = 1.8F; +static float g_mouse_sens = 0.12F; // deg per pixel -typedef struct { vec3 pos; float radius; float speed; } Target; +typedef struct +{ + vec3 pos; + float radius; + float speed; +} Target; static const int MAX_TARGETS = 128; -static Target g_targets[128]; -static int g_target_count = 0; -static float g_spawn_timer = 0.0f; -static float g_spawn_interval = 1.2f; // seconds -static int g_score = 0; +static Target g_targets[128]; +static int g_target_count = 0; +static float g_spawn_timer = 0.0F; +static float g_spawn_interval = 1.2F; // seconds +static int g_score = 0; -typedef enum { GAME_RUNNING = 0, GAME_OVER = 1 } GameState; +typedef enum +{ + GAME_RUNNING = 0, + GAME_OVER = 1 +} GameState; static GameState g_state = GAME_RUNNING; // Bullet visualization removed per request @@ -69,482 +91,634 @@ static int g_prev_ms = 0; // -------- Utility -------- static float clampf(float v, float lo, float hi) { return v < lo ? lo : (v > hi ? hi : v); } -static float deg2rad(float d) { return d * (float)M_PI / 180.0f; } +static float deg2rad(float d) { return d * (float)M_PI / 180.0F; } -static vec3 cam_front() +static vec3 cam_front(void) { - float yaw = deg2rad(g_yaw_deg); - float pitch = deg2rad(g_pitch_deg); - vec3 f = { cosf(pitch)*cosf(yaw), sinf(pitch), cosf(pitch)*sinf(yaw) }; - return v3_norm(f); + float yaw = deg2rad(g_yaw_deg); + float pitch = deg2rad(g_pitch_deg); + vec3 f = {cosf(pitch) * cosf(yaw), sinf(pitch), cosf(pitch) * sinf(yaw)}; + return v3_norm(f); } -static vec3 cam_right() -{ - return v3_norm(v3_cross(cam_front(), v3(0,1,0))); -} +static vec3 cam_right(void) { return v3_norm(v3_cross(cam_front(), v3(0, 1, 0))); } // -------- Audio (SDL2) -------- static SDL_AudioDeviceID g_audio_dev = 0; -static SDL_AudioSpec g_audio_have; -static bool g_audio_ok = false; +static SDL_AudioSpec g_audio_have; +static bool g_audio_ok = false; static void audio_cleanup(void) { - if (g_audio_dev) { - SDL_ClearQueuedAudio(g_audio_dev); - SDL_CloseAudioDevice(g_audio_dev); - g_audio_dev = 0; - } - if (g_audio_ok) { - SDL_QuitSubSystem(SDL_INIT_AUDIO); - g_audio_ok = false; - } + if (g_audio_dev) + { + SDL_ClearQueuedAudio(g_audio_dev); + SDL_CloseAudioDevice(g_audio_dev); + g_audio_dev = 0; + } + if (g_audio_ok) + { + SDL_QuitSubSystem(SDL_INIT_AUDIO); + g_audio_ok = false; + } } static void audio_init(void) { - if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) { - fprintf(stderr, "SDL audio init failed: %s\n", SDL_GetError()); - return; - } - SDL_AudioSpec want; - SDL_zero(want); - want.freq = 48000; - want.format = AUDIO_F32SYS; - want.channels = 1; - want.samples = 1024; - g_audio_dev = SDL_OpenAudioDevice(NULL, 0, &want, &g_audio_have, 0); - if (!g_audio_dev) { - fprintf(stderr, "SDL_OpenAudioDevice failed: %s\n", SDL_GetError()); - SDL_QuitSubSystem(SDL_INIT_AUDIO); - return; - } - SDL_PauseAudioDevice(g_audio_dev, 0); - g_audio_ok = true; - atexit(audio_cleanup); + if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) + { + fprintf(stderr, "SDL audio init failed: %s\n", SDL_GetError()); + return; + } + SDL_AudioSpec want; + SDL_zero(want); + want.freq = 48000; + want.format = AUDIO_F32SYS; + want.channels = 1; + want.samples = 1024; + g_audio_dev = SDL_OpenAudioDevice(NULL, 0, &want, &g_audio_have, 0); + if (!g_audio_dev) + { + fprintf(stderr, "SDL_OpenAudioDevice failed: %s\n", SDL_GetError()); + SDL_QuitSubSystem(SDL_INIT_AUDIO); + return; + } + SDL_PauseAudioDevice(g_audio_dev, 0); + g_audio_ok = true; + atexit(audio_cleanup); } -static void audio_queue_samples(const float* data, int frames) +static void audio_queue_samples(const float *data, int frames) { - if (!g_audio_ok) return; - int bytes = frames * (int)sizeof(float); - SDL_QueueAudio(g_audio_dev, data, bytes); + if (!g_audio_ok) + { + return; + } + int bytes = frames * (int)sizeof(float); + SDL_QueueAudio(g_audio_dev, data, bytes); } static void audio_play_tone(float freq, float duration, float vol) { - if (!g_audio_ok) return; - int sr = g_audio_have.freq ? g_audio_have.freq : 48000; - int frames = (int)(duration * sr); - if (frames <= 0) return; - float* buf = (float*)malloc((size_t)frames * sizeof(float)); - if (!buf) return; - float phase = 0.0f; - float dp = 2.0f * (float)M_PI * freq / (float)sr; - for (int i=0; i= MAX_TARGETS) return; - float radius_spawn = frand(22.0f, 34.0f); - float ang = frand(0.0f, 2.0f*(float)M_PI); - vec3 p = v3(cosf(ang)*radius_spawn, 0.5f, sinf(ang)*radius_spawn); - Target t; - t.pos = p; - t.radius = 0.6f; - t.speed = frand(1.4f, 3.2f); - g_targets[g_target_count++] = t; + if (g_target_count >= MAX_TARGETS) + { + return; + } + float radius_spawn = frand(22.0F, 34.0F); + float ang = frand(0.0F, 2.0F * (float)M_PI); + vec3 p = v3(cosf(ang) * radius_spawn, 0.5F, sinf(ang) * radius_spawn); + Target t; + t.pos = p; + t.radius = 0.6F; + t.speed = frand(1.4F, 3.2F); + g_targets[g_target_count++] = t; } -static void reset_game() +static void reset_game(void) { - g_score = 0; - g_spawn_timer = 0.0f; - g_spawn_interval = 1.2f; - clear_targets(); - // spawn a few to start - for (int i = 0; i < 4; ++i) spawn_target(); - g_state = GAME_RUNNING; - if (g_captured_mouse) { - g_ignore_next_passive = true; - glutWarpPointer(g_win_w/2, g_win_h/2); - } + g_score = 0; + g_spawn_timer = 0.0F; + g_spawn_interval = 1.2F; + clear_targets(); + // spawn a few to start + for (int i = 0; i < 4; ++i) + { + spawn_target(); + } + g_state = GAME_RUNNING; + if (g_captured_mouse) + { + g_ignore_next_passive = true; + glutWarpPointer(g_win_w / 2, g_win_h / 2); + } } // Ray-sphere intersection: returns t >= 0 for first hit, or -1 if miss static float ray_sphere(vec3 ro, vec3 rd, vec3 c, float r) { - vec3 oc = v3_sub(ro, c); - float b = v3_dot(oc, rd); - float cterm = v3_dot(oc, oc) - r*r; - float disc = b*b - cterm; - if (disc < 0.0f) return -1.0f; - float t = -b - sqrtf(disc); - return t >= 0.0f ? t : -1.0f; + vec3 oc = v3_sub(ro, c); + float b = v3_dot(oc, rd); + float cterm = v3_dot(oc, oc) - (r * r); + float disc = (b * b) - cterm; + if (disc < 0.0F) + { + return -1.0F; + } + float t = -b - sqrtf(disc); + return t >= 0.0F ? t : -1.0F; } -static void shoot() +static void shoot(void) { - if (g_state != GAME_RUNNING) return; - vec3 dir = cam_front(); - float best_t = 1e9f; - int best_i = -1; - for (int i = 0; i < g_target_count; ++i) { - float t = ray_sphere(g_cam_pos, dir, g_targets[i].pos, g_targets[i].radius); - if (t >= 0.0f && t < best_t) { best_t = t; best_i = i; } - } - audio_play_tone(1600.0f, 0.05f, 0.25f); // shoot + if (g_state != GAME_RUNNING) + { + return; + } + vec3 dir = cam_front(); + float best_t = 1e9F; + int best_i = -1; + for (int i = 0; i < g_target_count; ++i) + { + float t = ray_sphere(g_cam_pos, dir, g_targets[i].pos, g_targets[i].radius); + if (t >= 0.0F && t < best_t) + { + best_t = t; + best_i = i; + } + } + audio_play_tone(1600.0F, 0.05F, 0.25F); // shoot - if (best_i >= 0) { - // remove hit target (swap remove) - g_targets[best_i] = g_targets[g_target_count-1]; - g_target_count--; - g_score++; - audio_play_tone(600.0f, 0.08f, 0.35f); // hit - } + if (best_i >= 0) + { + // remove hit target (swap remove) + g_targets[best_i] = g_targets[g_target_count - 1]; + g_target_count--; + g_score++; + audio_play_tone(600.0F, 0.08F, 0.35F); // hit + } } // -------- Rendering -------- static void draw_grid(float half, float step) { - glColor3f(0.2f, 0.25f, 0.3f); - glBegin(GL_LINES); - for (float x = -half; x <= half + 1e-4f; x += step) { - glVertex3f(x, 0.0f, -half); glVertex3f(x, 0.0f, half); - } - for (float z = -half; z <= half + 1e-4f; z += step) { - glVertex3f(-half, 0.0f, z); glVertex3f(half, 0.0f, z); - } - glEnd(); + glColor3f(0.2F, 0.25F, 0.3F); + glBegin(GL_LINES); + for (float x = -half; x <= half + 1e-4F; x += step) + { + glVertex3f(x, 0.0F, -half); + glVertex3f(x, 0.0F, half); + } + for (float z = -half; z <= half + 1e-4F; z += step) + { + glVertex3f(-half, 0.0F, z); + glVertex3f(half, 0.0F, z); + } + glEnd(); } -static void draw_cube() +static void draw_cube(void) { - // Simple colored cube (size 1) - const float s = 0.5f; - glBegin(GL_QUADS); - // +X - glColor3f(1,0,0); glVertex3f( s,-s,-s); glVertex3f( s,-s, s); glVertex3f( s, s, s); glVertex3f( s, s,-s); - // -X - glColor3f(0.8f,0,0); glVertex3f(-s,-s,-s); glVertex3f(-s, s,-s); glVertex3f(-s, s, s); glVertex3f(-s,-s, s); - // +Y - glColor3f(0.9f,0.1f,0.1f); glVertex3f(-s, s,-s); glVertex3f( s, s,-s); glVertex3f( s, s, s); glVertex3f(-s, s, s); - // -Y - glColor3f(0.6f,0.05f,0.05f); glVertex3f(-s,-s,-s); glVertex3f(-s,-s, s); glVertex3f( s,-s, s); glVertex3f( s,-s,-s); - // +Z - glColor3f(1,0.2f,0.2f); glVertex3f(-s,-s, s); glVertex3f(-s, s, s); glVertex3f( s, s, s); glVertex3f( s,-s, s); - // -Z - glColor3f(0.7f,0.1f,0.1f); glVertex3f(-s,-s,-s); glVertex3f( s,-s,-s); glVertex3f( s, s,-s); glVertex3f(-s, s,-s); - glEnd(); + // Simple colored cube (size 1) + const float s = 0.5F; + glBegin(GL_QUADS); + // +X + glColor3f(1, 0, 0); + glVertex3f(s, -s, -s); + glVertex3f(s, -s, s); + glVertex3f(s, s, s); + glVertex3f(s, s, -s); + // -X + glColor3f(0.8F, 0, 0); + glVertex3f(-s, -s, -s); + glVertex3f(-s, s, -s); + glVertex3f(-s, s, s); + glVertex3f(-s, -s, s); + // +Y + glColor3f(0.9F, 0.1F, 0.1F); + glVertex3f(-s, s, -s); + glVertex3f(s, s, -s); + glVertex3f(s, s, s); + glVertex3f(-s, s, s); + // -Y + glColor3f(0.6F, 0.05F, 0.05F); + glVertex3f(-s, -s, -s); + glVertex3f(-s, -s, s); + glVertex3f(s, -s, s); + glVertex3f(s, -s, -s); + // +Z + glColor3f(1, 0.2F, 0.2F); + glVertex3f(-s, -s, s); + glVertex3f(-s, s, s); + glVertex3f(s, s, s); + glVertex3f(s, -s, s); + // -Z + glColor3f(0.7F, 0.1F, 0.1F); + glVertex3f(-s, -s, -s); + glVertex3f(s, -s, -s); + glVertex3f(s, s, -s); + glVertex3f(-s, s, -s); + glEnd(); } -static void draw_crosshair() +static void draw_crosshair(void) { - glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); - gluOrtho2D(0, g_win_w, g_win_h, 0); - glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + gluOrtho2D(0, g_win_w, g_win_h, 0); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); - glDisable(GL_DEPTH_TEST); - glColor3f(0.95f, 0.95f, 0.95f); - int cx = g_win_w/2, cy = g_win_h/2; - int s = 8; - glBegin(GL_LINES); - glVertex2i(cx - s, cy); glVertex2i(cx + s, cy); - glVertex2i(cx, cy - s); glVertex2i(cx, cy + s); - glEnd(); - glEnable(GL_DEPTH_TEST); + glDisable(GL_DEPTH_TEST); + glColor3f(0.95F, 0.95F, 0.95F); + int cx = g_win_w / 2; + int cy = g_win_h / 2; + int s = 8; + glBegin(GL_LINES); + glVertex2i(cx - s, cy); + glVertex2i(cx + s, cy); + glVertex2i(cx, cy - s); + glVertex2i(cx, cy + s); + glEnd(); + glEnable(GL_DEPTH_TEST); - glMatrixMode(GL_MODELVIEW); glPopMatrix(); - glMatrixMode(GL_PROJECTION); glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); } -static void display() +static void display(void) { - glClearColor(0.05f, 0.06f, 0.08f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glClearColor(0.05F, 0.06F, 0.08F, 1.0F); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); - vec3 front = cam_front(); - vec3 at = v3_add(g_cam_pos, front); - gluLookAt(g_cam_pos.x, g_cam_pos.y, g_cam_pos.z, - at.x, at.y, at.z, - 0, 1, 0); + vec3 front = cam_front(); + vec3 at = v3_add(g_cam_pos, front); + gluLookAt(g_cam_pos.x, g_cam_pos.y, g_cam_pos.z, at.x, at.y, at.z, 0, 1, 0); - // Ground grid - draw_grid(40.0f, 1.0f); + // Ground grid + draw_grid(40.0F, 1.0F); - // Target cubes - for (int i = 0; i < g_target_count; ++i) { - glPushMatrix(); - glTranslatef(g_targets[i].pos.x, g_targets[i].pos.y, g_targets[i].pos.z); - draw_cube(); - glPopMatrix(); - } + // Target cubes + for (int i = 0; i < g_target_count; ++i) + { + glPushMatrix(); + glTranslatef(g_targets[i].pos.x, g_targets[i].pos.y, g_targets[i].pos.z); + draw_cube(); + glPopMatrix(); + } - // Bullet line removed + // Bullet line removed - // Crosshair overlay (only during gameplay) - if (g_state == GAME_RUNNING) { - draw_crosshair(); - } + // Crosshair overlay (only during gameplay) + if (g_state == GAME_RUNNING) + { + draw_crosshair(); + } - // Game over overlay text - if (g_state == GAME_OVER) { - // Switch to 2D for text - glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); - gluOrtho2D(0, g_win_w, g_win_h, 0); - glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); - glDisable(GL_DEPTH_TEST); + // Game over overlay text + if (g_state == GAME_OVER) + { + // Switch to 2D for text + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + gluOrtho2D(0, g_win_w, g_win_h, 0); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glDisable(GL_DEPTH_TEST); - const char* line1 = "GAME OVER"; - char line2[64]; snprintf(line2, sizeof(line2), "Score: %d", g_score); - const char* line3 = "Press R to restart or Esc to quit"; + const char *line1 = "GAME OVER"; + char line2[64]; + snprintf(line2, sizeof(line2), "Score: %d", g_score); + const char *line3 = "Press R to restart or Esc to quit"; - void* font = GLUT_BITMAP_HELVETICA_18; - int cx = g_win_w/2; int cy = g_win_h/2; - glColor3f(1,1,1); - // naive center: estimate width by char count * 9 px - int w1 = (int)(9 * strlen(line1)); - int w2 = (int)(9 * strlen(line2)); - int w3 = (int)(9 * strlen(line3)); + void *font = GLUT_BITMAP_HELVETICA_18; + int cx = g_win_w / 2; + int cy = g_win_h / 2; + glColor3f(1, 1, 1); + // naive center: estimate width by char count * 9 px + int w1 = (int)(9 * strlen(line1)); + int w2 = (int)(9 * strlen(line2)); + int w3 = (int)(9 * strlen(line3)); - glRasterPos2i(cx - w1/2, cy - 30); - for (const char* p=line1; *p; ++p) glutBitmapCharacter(font, *p); - glRasterPos2i(cx - w2/2, cy - 8); - for (const char* p=line2; *p; ++p) glutBitmapCharacter(font, *p); - glRasterPos2i(cx - w3/2, cy + 18); - for (const char* p=line3; *p; ++p) glutBitmapCharacter(font, *p); + glRasterPos2i(cx - (w1 / 2), cy - 30); + for (const char *p = line1; *p; ++p) + { + glutBitmapCharacter(font, *p); + } + glRasterPos2i(cx - (w2 / 2), cy - 8); + for (const char *p = line2; *p; ++p) + { + glutBitmapCharacter(font, *p); + } + glRasterPos2i(cx - (w3 / 2), cy + 18); + for (const char *p = line3; *p; ++p) + { + glutBitmapCharacter(font, *p); + } - glEnable(GL_DEPTH_TEST); - glMatrixMode(GL_MODELVIEW); glPopMatrix(); - glMatrixMode(GL_PROJECTION); glPopMatrix(); - } + glEnable(GL_DEPTH_TEST); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + } - glutSwapBuffers(); + glutSwapBuffers(); } // -------- Update/Input -------- static void update(float dt) { - if (g_state == GAME_RUNNING) { - float speed = g_move_speed * ((g_keys['\t'] || g_keys['q']) ? g_sprint_mul : 1.0f); // Tab/q to sprint - vec3 f = cam_front(); f.y = 0.0f; f = v3_norm(f); - vec3 r = cam_right(); r.y = 0.0f; r = v3_norm(r); - // Accumulate movement and apply once to avoid drift artifacts - vec3 mv = v3(0,0,0); - if (g_keys['w']) mv = v3_add(mv, f); - if (g_keys['s']) mv = v3_sub(mv, f); - if (g_keys['a']) mv = v3_sub(mv, r); - if (g_keys['d']) mv = v3_add(mv, r); - if (v3_len(mv) > 0.0f) { - mv = v3_norm(mv); - g_cam_pos = v3_add(g_cam_pos, v3_scale(mv, speed*dt)); - } + if (g_state == GAME_RUNNING) + { + float speed = + g_move_speed * ((g_keys['\t'] || g_keys['q']) ? g_sprint_mul : 1.0F); // Tab/q to sprint + vec3 f = cam_front(); + f.y = 0.0F; + f = v3_norm(f); + vec3 r = cam_right(); + r.y = 0.0F; + r = v3_norm(r); + // Accumulate movement and apply once to avoid drift artifacts + vec3 mv = v3(0, 0, 0); + if (g_keys['w']) + { + mv = v3_add(mv, f); + } + if (g_keys['s']) + { + mv = v3_sub(mv, f); + } + if (g_keys['a']) + { + mv = v3_sub(mv, r); + } + if (g_keys['d']) + { + mv = v3_add(mv, r); + } + if (v3_len(mv) > 0.0F) + { + mv = v3_norm(mv); + g_cam_pos = v3_add(g_cam_pos, v3_scale(mv, speed * dt)); + } - // Keep feet on ground - g_cam_pos.y = 1.6f; + // Keep feet on ground + g_cam_pos.y = 1.6F; - // Move targets toward player and check collision - for (int i = 0; i < g_target_count; ++i) { - vec3 to_player = v3_sub(g_cam_pos, g_targets[i].pos); - to_player.y = 0.0f; - vec3 dir = v3_norm(to_player); - g_targets[i].pos = v3_add(g_targets[i].pos, v3_scale(dir, g_targets[i].speed * dt)); - g_targets[i].pos.y = 0.5f; + // Move targets toward player and check collision + for (int i = 0; i < g_target_count; ++i) + { + vec3 to_player = v3_sub(g_cam_pos, g_targets[i].pos); + to_player.y = 0.0F; + vec3 dir = v3_norm(to_player); + g_targets[i].pos = v3_add(g_targets[i].pos, v3_scale(dir, g_targets[i].speed * dt)); + g_targets[i].pos.y = 0.5F; - float dist2 = to_player.x*to_player.x + to_player.z*to_player.z; - float reach = (0.6f + g_targets[i].radius); - if (dist2 <= reach*reach) { - g_state = GAME_OVER; - glutSetCursor(GLUT_CURSOR_LEFT_ARROW); - audio_play_sweep(400.0f, 120.0f, 0.5f, 0.5f); // game over - break; - } - } + float dist2 = (to_player.x * to_player.x) + (to_player.z * to_player.z); + float reach = (0.6F + g_targets[i].radius); + if (dist2 <= reach * reach) + { + g_state = GAME_OVER; + glutSetCursor(GLUT_CURSOR_LEFT_ARROW); + audio_play_sweep(400.0F, 120.0F, 0.5F, 0.5F); // game over + break; + } + } - // Spawn new targets over time (slightly accelerate spawn rate) - g_spawn_timer += dt; - if (g_spawn_timer >= g_spawn_interval) { - g_spawn_timer = 0.0f; - spawn_target(); - g_spawn_interval = fmaxf(0.4f, g_spawn_interval * 0.98f); - } - } + // Spawn new targets over time (slightly accelerate spawn rate) + g_spawn_timer += dt; + if (g_spawn_timer >= g_spawn_interval) + { + g_spawn_timer = 0.0F; + spawn_target(); + g_spawn_interval = fmaxf(0.4F, g_spawn_interval * 0.98F); + } + } - // No bullet tracer timer + // No bullet tracer timer } -static void idle() +static void idle(void) { - int now = glutGet(GLUT_ELAPSED_TIME); - if (g_prev_ms == 0) g_prev_ms = now; - float dt = (now - g_prev_ms) / 1000.0f; - g_prev_ms = now; + int now = glutGet(GLUT_ELAPSED_TIME); + if (g_prev_ms == 0) + { + g_prev_ms = now; + } + float dt = (now - g_prev_ms) / 1000.0F; + g_prev_ms = now; - update(dt); - glutPostRedisplay(); + update(dt); + glutPostRedisplay(); } static void reshape(int w, int h) { - g_win_w = w > 1 ? w : 1; - g_win_h = h > 1 ? h : 1; - glViewport(0, 0, g_win_w, g_win_h); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(75.0, (double)g_win_w/(double)g_win_h, 0.05, 500.0); - glMatrixMode(GL_MODELVIEW); + g_win_w = w > 1 ? w : 1; + g_win_h = h > 1 ? h : 1; + glViewport(0, 0, g_win_w, g_win_h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(75.0, (double)g_win_w / (double)g_win_h, 0.05, 500.0); + glMatrixMode(GL_MODELVIEW); - if (g_captured_mouse && g_state == GAME_RUNNING) { - g_ignore_next_passive = true; - glutWarpPointer(g_win_w/2, g_win_h/2); - } + if (g_captured_mouse && g_state == GAME_RUNNING) + { + g_ignore_next_passive = true; + glutWarpPointer(g_win_w / 2, g_win_h / 2); + } } static void keyboard_down(unsigned char key, int x, int y) { - (void)x; (void)y; - unsigned char k = (unsigned char)tolower(key); - g_keys[k] = true; - if (key == 27) { // Esc - exit(0); - } else if (key == ' ') { - shoot(); - } else if (key == 'm' || key == 'M') { - g_captured_mouse = !g_captured_mouse; - glutSetCursor(g_captured_mouse ? GLUT_CURSOR_NONE : GLUT_CURSOR_LEFT_ARROW); - if (g_captured_mouse && g_state == GAME_RUNNING) { - g_ignore_next_passive = true; - glutWarpPointer(g_win_w/2, g_win_h/2); - } - } else if (key == 'r' || key == 'R') { - if (g_state == GAME_OVER) { - glutSetCursor(g_captured_mouse ? GLUT_CURSOR_NONE : GLUT_CURSOR_LEFT_ARROW); - reset_game(); - } - } + (void)x; + (void)y; + unsigned char k = (unsigned char)tolower(key); + g_keys[k] = true; + if (key == 27) + { // Esc + exit(0); + } + else if (key == ' ') + { + shoot(); + } + else if (key == 'm' || key == 'M') + { + g_captured_mouse = !g_captured_mouse; + glutSetCursor(g_captured_mouse ? GLUT_CURSOR_NONE : GLUT_CURSOR_LEFT_ARROW); + if (g_captured_mouse && g_state == GAME_RUNNING) + { + g_ignore_next_passive = true; + glutWarpPointer(g_win_w / 2, g_win_h / 2); + } + } + else if (key == 'r' || key == 'R') + { + if (g_state == GAME_OVER) + { + glutSetCursor(g_captured_mouse ? GLUT_CURSOR_NONE : GLUT_CURSOR_LEFT_ARROW); + reset_game(); + } + } } static void keyboard_up(unsigned char key, int x, int y) { - (void)x; (void)y; - unsigned char k = (unsigned char)tolower(key); - g_keys[k] = false; + (void)x; + (void)y; + unsigned char k = (unsigned char)tolower(key); + g_keys[k] = false; } static void mouse_button(int button, int state, int x, int y) { - (void)x; (void)y; - if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { - shoot(); - } + (void)x; + (void)y; + if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) + { + shoot(); + } } static void passive_motion(int x, int y) { - if (!g_captured_mouse || g_state != GAME_RUNNING) return; + if (!g_captured_mouse || g_state != GAME_RUNNING) + { + return; + } - if (g_ignore_next_passive) { // ignore event caused by warp - g_ignore_next_passive = false; - return; - } + if (g_ignore_next_passive) + { // ignore event caused by warp + g_ignore_next_passive = false; + return; + } - if (g_last_mouse_x < 0) { g_last_mouse_x = x; g_last_mouse_y = y; } - int dx = x - g_win_w/2; - int dy = y - g_win_h/2; + if (g_last_mouse_x < 0) + { + g_last_mouse_x = x; + g_last_mouse_y = y; + } + int dx = x - (g_win_w / 2); + int dy = y - (g_win_h / 2); - g_yaw_deg += dx * g_mouse_sens; - g_pitch_deg -= dy * g_mouse_sens; - g_pitch_deg = clampf(g_pitch_deg, -89.0f, 89.0f); + g_yaw_deg += dx * g_mouse_sens; + g_pitch_deg -= dy * g_mouse_sens; + g_pitch_deg = clampf(g_pitch_deg, -89.0F, 89.0F); - g_ignore_next_passive = true; - glutWarpPointer(g_win_w/2, g_win_h/2); + g_ignore_next_passive = true; + glutWarpPointer(g_win_w / 2, g_win_h / 2); } // -------- Init -------- -static void init_gl() +static void init_gl(void) { - glEnable(GL_DEPTH_TEST); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glLineWidth(1.0f); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glLineWidth(1.0F); } static void on_entry(int state) { - if (state != GLUT_ENTERED) { - for (int i = 0; i < 256; ++i) g_keys[i] = false; - } + if (state != GLUT_ENTERED) + { + for (int i = 0; i < 256; ++i) + { + g_keys[i] = false; + } + } } -int main(int argc, char** argv) +int main(int argc, char **argv) { - (void)argv; - srand((unsigned)time(NULL)); + (void)argv; + srand((unsigned)time(NULL)); - glutInit(&argc, argv); - glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); - glutInitWindowSize(g_win_w, g_win_h); - glutCreateWindow("FPS Demo"); + glutInit(&argc, argv); + glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); + glutInitWindowSize(g_win_w, g_win_h); + glutCreateWindow("FPS Demo"); - init_gl(); - audio_init(); + init_gl(); + audio_init(); - glutDisplayFunc(display); - glutIdleFunc(idle); - glutReshapeFunc(reshape); - glutKeyboardFunc(keyboard_down); - glutKeyboardUpFunc(keyboard_up); - // Clear keys on focus leave to avoid stuck inputs - glutEntryFunc(on_entry); - glutPassiveMotionFunc(passive_motion); - glutMouseFunc(mouse_button); + glutDisplayFunc(display); + glutIdleFunc(idle); + glutReshapeFunc(reshape); + glutKeyboardFunc(keyboard_down); + glutKeyboardUpFunc(keyboard_up); + // Clear keys on focus leave to avoid stuck inputs + glutEntryFunc(on_entry); + glutPassiveMotionFunc(passive_motion); + glutMouseFunc(mouse_button); - // Start with mouse captured - glutSetCursor(GLUT_CURSOR_NONE); - g_ignore_next_passive = true; - glutWarpPointer(g_win_w/2, g_win_h/2); + // Start with mouse captured + glutSetCursor(GLUT_CURSOR_NONE); + g_ignore_next_passive = true; + glutWarpPointer(g_win_w / 2, g_win_h / 2); - reset_game(); + reset_game(); - glutMainLoop(); - return 0; + glutMainLoop(); + return 0; } diff --git a/C/imageViewer/main.c b/C/imageViewer/main.c index e0d6106..35f32fd 100644 --- a/C/imageViewer/main.c +++ b/C/imageViewer/main.c @@ -1,5 +1,17 @@ #include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -7,17 +19,16 @@ #include #include -#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; + } } } diff --git a/C/lichess_random_engine/main.c b/C/lichess_random_engine/main.c index 6e0e464..72f985b 100644 --- a/C/lichess_random_engine/main.c +++ b/C/lichess_random_engine/main.c @@ -2,7 +2,8 @@ // Contract expected by PYTHON/lichess_bot/engine.py: // - Usage without explanation: random_engine --fen "" ... // -> prints the chosen UCI move on stdout -// - With explanation: random_engine --fen "" --explain [--analyze ] ... +// - With explanation: random_engine --fen "" --explain [--analyze ] +// ... // -> 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 #include #include #include -#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 '' [--explain] [--analyze ] \n", - prog); +static void print_usage(const char *prog) +{ + fprintf(stderr, "Usage: %s --fen '' [--explain] [--analyze ] \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 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; } - diff --git a/C/lichess_random_engine/micro_max.c b/C/lichess_random_engine/micro_max.c index e4a7934..fa410f0 100644 --- a/C/lichess_random_engine/micro_max.c +++ b/C/lichess_random_engine/micro_max.c @@ -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] ... // 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 #include #include #include #include #include -#include -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] \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= 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] \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; } \ No newline at end of file diff --git a/C/lichess_random_engine/movegen.c b/C/lichess_random_engine/movegen.c index ead9146..b07a470 100644 --- a/C/lichess_random_engine/movegen.c +++ b/C/lichess_random_engine/movegen.c @@ -1,164 +1,368 @@ #include "movegen.h" -#include -#include #include +#include #include +#include static inline int on_board(int sq) { return (sq & 0x88) == 0; } static inline int rank_of(int sq) { return sq >> 4; } static inline int file_of(int sq) { return sq & 7; } -static inline int color_of(Piece p){ return (p>=BP); } -static inline int is_white(Piece p){ return p>=WP && p<=WK; } -static inline int is_black(Piece p){ return p>=BP && p<=BK; } +static inline int color_of(Piece p) { return (p >= BP); } +static inline int is_white(Piece p) { return p >= WP && p <= WK; } +static inline int is_black(Piece p) { return p >= BP && p <= BK; } -static Piece make_piece(char c){ - switch(c){ - case 'P': return WP; case 'N': return WN; case 'B': return WB; case 'R': return WR; case 'Q': return WQ; case 'K': return WK; - case 'p': return BP; case 'n': return BN; case 'b': return BB; case 'r': return BR; case 'q': return BQ; case 'k': return BK; - default: return EMPTY; +static Piece make_piece(char c) +{ + switch (c) + { + case 'P': + return WP; + case 'N': + return WN; + case 'B': + return WB; + case 'R': + return WR; + case 'Q': + return WQ; + case 'K': + return WK; + case 'p': + return BP; + case 'n': + return BN; + case 'b': + return BB; + case 'r': + return BR; + case 'q': + return BQ; + case 'k': + return BK; + default: + return EMPTY; } } -static char piece_to_char(Piece p){ - switch(p){ - case WP: return 'P'; case WN: return 'N'; case WB: return 'B'; case WR: return 'R'; case WQ: return 'Q'; case WK: return 'K'; - case BP: return 'p'; case BN: return 'n'; case BB: return 'b'; case BR: return 'r'; case BQ: return 'q'; case BK: return 'k'; - default: return '.'; +static char piece_to_char(Piece p) +{ + switch (p) + { + case WP: + return 'P'; + case WN: + return 'N'; + case WB: + return 'B'; + case WR: + return 'R'; + case WQ: + return 'Q'; + case WK: + return 'K'; + case BP: + return 'p'; + case BN: + return 'n'; + case BB: + return 'b'; + case BR: + return 'r'; + case BQ: + return 'q'; + case BK: + return 'k'; + default: + return '.'; } } -void set_startpos(Position *pos){ +void set_startpos(Position *pos) +{ memset(pos, 0, sizeof(*pos)); - for(int i=0;iboard[i]=EMPTY; + for (int i = 0; i < BOARD_SIZE; i++) + { + pos->board[i] = EMPTY; + } const char *start = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"; - char fen[128]; strcpy(fen, start); + char fen[128]; + strcpy(fen, start); strcat(fen, " w KQkq - 0 1"); parse_fen(pos, fen); } -int parse_fen(Position *pos, const char *fen){ +int parse_fen(Position *pos, const char *fen) +{ memset(pos, 0, sizeof(*pos)); - for(int i=0;iboard[i]=EMPTY; - pos->ep_square = -1; - pos->castle = 0; - pos->halfmove_clock = 0; + for (int i = 0; i < BOARD_SIZE; i++) + { + pos->board[i] = EMPTY; + } + pos->ep_square = -1; + pos->castle = 0; + pos->halfmove_clock = 0; pos->fullmove_number = 1; // pieces - int sq = 0x70; // A8 - const char *p = fen; - while (*p && *p!=' ') { - if (*p=='/') { sq = (sq & 0x70) - 0x10; p++; continue; } - if (isdigit((unsigned char)*p)) { sq += (*p - '0'); p++; continue; } + int sq = 0x70; // A8 + const char *p = fen; + while (*p && *p != ' ') + { + if (*p == '/') + { + sq = (sq & 0x70) - 0x10; + p++; + continue; + } + if (isdigit((unsigned char)*p)) + { + sq += (*p - '0'); + p++; + continue; + } Piece pc = make_piece(*p++); - if (!on_board(sq)) return 0; + if (!on_board(sq)) + { + return 0; + } pos->board[sq++] = pc; } - if (*p!=' ') return 0; + if (*p != ' ') + { + return 0; + } p++; // side - if (*p=='w') pos->side = WHITE; else if (*p=='b') pos->side = BLACK; else return 0; p++; - if (*p!=' ') return 0; + if (*p == 'w') + { + pos->side = WHITE; + } + else if (*p == 'b') + { + pos->side = BLACK; + } + else + { + return 0; + } + p++; + if (*p != ' ') + { + return 0; + } p++; // castling - if (*p=='-') { p++; } - else { - while (*p && *p!=' ') { - if (*p=='K') pos->castle |= 1<<0; - else if (*p=='Q') pos->castle |= 1<<1; - else if (*p=='k') pos->castle |= 1<<2; - else if (*p=='q') pos->castle |= 1<<3; - else return 0; + if (*p == '-') + { + p++; + } + else + { + while (*p && *p != ' ') + { + if (*p == 'K') + { + pos->castle |= 1 << 0; + } + else if (*p == 'Q') + { + pos->castle |= 1 << 1; + } + else if (*p == 'k') + { + pos->castle |= 1 << 2; + } + else if (*p == 'q') + { + pos->castle |= 1 << 3; + } + else + { + return 0; + } p++; } } - if (*p!=' ') return 0; + if (*p != ' ') + { + return 0; + } p++; // en-passant - if (*p=='-') { pos->ep_square = -1; p++; } - else { - if (p[0]>='a' && p[0]<='h' && p[1]>='1' && p[1]<='8'){ - int f = p[0]-'a'; int r = p[1]-'1'; - pos->ep_square = (r<<4) | f; - p+=2; - } else return 0; + if (*p == '-') + { + pos->ep_square = -1; + p++; + } + else + { + if (p[0] >= 'a' && p[0] <= 'h' && p[1] >= '1' && p[1] <= '8') + { + int f = p[0] - 'a'; + int r = p[1] - '1'; + pos->ep_square = (r << 4) | f; + p += 2; + } + else + { + return 0; + } + } + if (*p == ' ') + { + p++; } - if (*p==' ') p++; // halfmove clock - if (isdigit((unsigned char)*p)) { - pos->halfmove_clock = strtol(p, (char**)&p, 10); + if (isdigit((unsigned char)*p)) + { + pos->halfmove_clock = strtol(p, (char **)&p, 10); + } + if (*p == ' ') + { + p++; } - if (*p==' ') p++; // fullmove number - if (isdigit((unsigned char)*p)) pos->fullmove_number = strtol(p, NULL, 10); + if (isdigit((unsigned char)*p)) + { + pos->fullmove_number = strtol(p, NULL, 10); + } return 1; } -static int add_move(Move *moves, int count, int max, int from, int to, int cap, int promo, int ep, int castle){ - if (count>=max) return count; - Move m; m.from=(uint8_t)from; m.to=(uint8_t)to; m.promo=(uint8_t)promo; m.is_capture=(uint8_t)cap; m.is_enpassant=(uint8_t)ep; m.is_castle=(uint8_t)castle; +static int add_move(Move *moves, int count, int max, int from, int to, int cap, int promo, int ep, + int castle) +{ + if (count >= max) + { + return count; + } + Move m; + m.from = (uint8_t)from; + m.to = (uint8_t)to; + m.promo = (uint8_t)promo; + m.is_capture = (uint8_t)cap; + m.is_enpassant = (uint8_t)ep; + m.is_castle = (uint8_t)castle; moves[count++] = m; return count; } // Check detection via attack lookup -static int square_attacked_by(const Position *pos, int sq, Color by){ +static int square_attacked_by(const Position *pos, int sq, Color by) +{ // Knights - static const int kn[] = {33,31,18,14,-33,-31,-18,-14}; - for(int i=0;i<8;i++){ + static const int kn[] = {33, 31, 18, 14, -33, -31, -18, -14}; + for (int i = 0; i < 8; i++) + { int s = sq + kn[i]; - if (!on_board(s)) continue; + if (!on_board(s)) + { + continue; + } Piece p = pos->board[s]; - if (by==WHITE && p==WN) return 1; - if (by==BLACK && p==BN) return 1; + if (by == WHITE && p == WN) + { + return 1; + } + if (by == BLACK && p == BN) + { + return 1; + } } // Kings - static const int kd[] = {1,-1,16,-16,17,15,-17,-15}; - for(int i=0;i<8;i++){ - int s = sq + kd[i]; if (!on_board(s)) continue; Piece p=pos->board[s]; - if (by==WHITE && p==WK) return 1; - if (by==BLACK && p==BK) return 1; + static const int kd[] = {1, -1, 16, -16, 17, 15, -17, -15}; + for (int i = 0; i < 8; i++) + { + int s = sq + kd[i]; + if (!on_board(s)) + { + continue; + } + Piece p = pos->board[s]; + if (by == WHITE && p == WK) + { + return 1; + } + if (by == BLACK && p == BK) + { + return 1; + } } // Pawns - if (by==WHITE){ - int s1 = sq - 15; int s2 = sq - 17; // white pawns attack up-left/up-right from their perspective - if (on_board(s1) && pos->board[s1]==WP) return 1; - if (on_board(s2) && pos->board[s2]==WP) return 1; - } else { - int s1 = sq + 15; int s2 = sq + 17; - if (on_board(s1) && pos->board[s1]==BP) return 1; - if (on_board(s2) && pos->board[s2]==BP) return 1; + if (by == WHITE) + { + int s1 = sq - 15; + int s2 = sq - 17; // white pawns attack up-left/up-right from their perspective + if (on_board(s1) && pos->board[s1] == WP) + { + return 1; + } + if (on_board(s2) && pos->board[s2] == WP) + { + return 1; + } + } + else + { + int s1 = sq + 15; + int s2 = sq + 17; + if (on_board(s1) && pos->board[s1] == BP) + { + return 1; + } + if (on_board(s2) && pos->board[s2] == BP) + { + return 1; + } } // Sliders: bishops/queens diagonals - static const int bd[] = {17,15,-17,-15}; - for (int d=0; d<4; ++d){ + static const int bd[] = {17, 15, -17, -15}; + for (int d = 0; d < 4; ++d) + { int s = sq + bd[d]; - while(on_board(s)){ + while (on_board(s)) + { Piece p = pos->board[s]; - if (p!=EMPTY){ - if (by==WHITE && (p==WB || p==WQ)) return 1; - if (by==BLACK && (p==BB || p==BQ)) return 1; + if (p != EMPTY) + { + if (by == WHITE && (p == WB || p == WQ)) + { + return 1; + } + if (by == BLACK && (p == BB || p == BQ)) + { + return 1; + } break; } s += bd[d]; } } // Rooks/queens - static const int rd[] = {1,-1,16,-16}; - for (int d=0; d<4; ++d){ + static const int rd[] = {1, -1, 16, -16}; + for (int d = 0; d < 4; ++d) + { int s = sq + rd[d]; - while(on_board(s)){ + while (on_board(s)) + { Piece p = pos->board[s]; - if (p!=EMPTY){ - if (by==WHITE && (p==WR || p==WQ)) return 1; - if (by==BLACK && (p==BR || p==BQ)) return 1; + if (p != EMPTY) + { + if (by == WHITE && (p == WR || p == WQ)) + { + return 1; + } + if (by == BLACK && (p == BR || p == BQ)) + { + return 1; + } break; } s += rd[d]; @@ -167,290 +371,611 @@ static int square_attacked_by(const Position *pos, int sq, Color by){ return 0; } -int in_check(const Position *pos, Color side){ +int in_check(const Position *pos, Color side) +{ // find king square - Piece k = (side==WHITE)?WK:BK; - int ks = -1; - for (int sq=0; sqboard[sq]==k){ ks=sq; break; } } - if (ks<0) return 0; - return square_attacked_by(pos, ks, (side==WHITE)?BLACK:WHITE); + Piece k = (side == WHITE) ? WK : BK; + int ks = -1; + for (int sq = 0; sq < BOARD_SIZE; ++sq) + { + if (!on_board(sq)) + { + sq = (sq | 7); + continue; + } + if (pos->board[sq] == k) + { + ks = sq; + break; + } + } + if (ks < 0) + { + return 0; + } + return square_attacked_by(pos, ks, (side == WHITE) ? BLACK : WHITE); } -static int gen_moves_internal(const Position *pos, Move *moves, int max_moves, int captures_only){ - int count = 0; - Color us = pos->side; - int forward = (us==WHITE)?16:-16; - int start_rank = (us==WHITE)?1:6; - int promo_rank = (us==WHITE)?6:1; // rank before promotion move (from rank) +static int gen_moves_internal(const Position *pos, Move *moves, int max_moves, int captures_only) +{ + int count = 0; + Color us = pos->side; + int forward = (us == WHITE) ? 16 : -16; + int start_rank = (us == WHITE) ? 1 : 6; + int promo_rank = (us == WHITE) ? 6 : 1; // rank before promotion move (from rank) - for (int sq=0; sqboard[sq]; - if (p==EMPTY) continue; - if ((us==WHITE && !is_white(p)) || (us==BLACK && !is_black(p))) continue; + if (p == EMPTY) + { + continue; + } + if ((us == WHITE && !is_white(p)) || (us == BLACK && !is_black(p))) + { + continue; + } - switch(p){ - case WP: case BP: { - int dir = (p==WP)?16:-16; - int r = rank_of(sq); - // quiet pushes - if (!captures_only){ - int to = sq + dir; if (on_board(to) && pos->board[to]==EMPTY){ - if (r==promo_rank){ - count = add_move(moves, count, max_moves, sq, to, 0, (us==WHITE?WQ:BQ), 0, 0); - count = add_move(moves, count, max_moves, sq, to, 0, (us==WHITE?WR:BR), 0, 0); - count = add_move(moves, count, max_moves, sq, to, 0, (us==WHITE?WB:BB), 0, 0); - count = add_move(moves, count, max_moves, sq, to, 0, (us==WHITE?WN:BN), 0, 0); - } else { - count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); - // double push from start rank - if (r==start_rank){ - int to2 = to + dir; if (on_board(to2) && pos->board[to2]==EMPTY){ - count = add_move(moves, count, max_moves, sq, to2, 0, 0, 0, 0); - } + switch (p) + { + case WP: + case BP: + { + int dir = (p == WP) ? 16 : -16; + int r = rank_of(sq); + // quiet pushes + if (!captures_only) + { + int to = sq + dir; + if (on_board(to) && pos->board[to] == EMPTY) + { + if (r == promo_rank) + { + count = add_move(moves, count, max_moves, sq, to, 0, + (us == WHITE ? WQ : BQ), 0, 0); + count = add_move(moves, count, max_moves, sq, to, 0, + (us == WHITE ? WR : BR), 0, 0); + count = add_move(moves, count, max_moves, sq, to, 0, + (us == WHITE ? WB : BB), 0, 0); + count = add_move(moves, count, max_moves, sq, to, 0, + (us == WHITE ? WN : BN), 0, 0); + } + else + { + count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); + // double push from start rank + if (r == start_rank) + { + int to2 = to + dir; + if (on_board(to2) && pos->board[to2] == EMPTY) + { + count = add_move(moves, count, max_moves, sq, to2, 0, 0, 0, 0); } } } } - // captures - int caps[2] = { sq + dir + 1, sq + dir - 1 }; - for (int i=0;i<2;i++){ - int to = caps[i]; if (!on_board(to)) continue; Piece tp = pos->board[to]; - if (tp!=EMPTY && color_of(tp)!=us){ - if (r==promo_rank){ - count = add_move(moves, count, max_moves, sq, to, 1, (us==WHITE?WQ:BQ), 0, 0); - count = add_move(moves, count, max_moves, sq, to, 1, (us==WHITE?WR:BR), 0, 0); - count = add_move(moves, count, max_moves, sq, to, 1, (us==WHITE?WB:BB), 0, 0); - count = add_move(moves, count, max_moves, sq, to, 1, (us==WHITE?WN:BN), 0, 0); - } else { + } + // captures + int caps[2] = {sq + dir + 1, sq + dir - 1}; + for (int i = 0; i < 2; i++) + { + int to = caps[i]; + if (!on_board(to)) + { + continue; + } + Piece tp = pos->board[to]; + if (tp != EMPTY && color_of(tp) != us) + { + if (r == promo_rank) + { + count = add_move(moves, count, max_moves, sq, to, 1, + (us == WHITE ? WQ : BQ), 0, 0); + count = add_move(moves, count, max_moves, sq, to, 1, + (us == WHITE ? WR : BR), 0, 0); + count = add_move(moves, count, max_moves, sq, to, 1, + (us == WHITE ? WB : BB), 0, 0); + count = add_move(moves, count, max_moves, sq, to, 1, + (us == WHITE ? WN : BN), 0, 0); + } + else + { + count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0); + } + } + } + // en-passant + if (pos->ep_square >= 0) + { + for (int i = 0; i < 2; i++) + { + int to = caps[i]; + if (!on_board(to)) + { + continue; + } + if (to == pos->ep_square) + { + count = add_move(moves, count, max_moves, sq, to, 1, 0, 1, 0); + } + } + } + } + break; + case WN: + case BN: + { + static const int d[8] = {33, 31, 18, 14, -33, -31, -18, -14}; + for (int i = 0; i < 8; i++) + { + int to = sq + d[i]; + if (!on_board(to)) + { + continue; + } + Piece tp = pos->board[to]; + if (tp == EMPTY) + { + if (!captures_only) + { + count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); + } + } + else if (color_of(tp) != us) + { + count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0); + } + } + } + break; + case WB: + case BB: + case WR: + case BR: + case WQ: + case BQ: + { + static const int bd[4] = {17, 15, -17, -15}; + static const int rd[4] = {1, -1, 16, -16}; + const int *dirs = NULL; + int ndirs = 0; + if (p == WB || p == BB) + { + dirs = bd; + ndirs = 4; + } + else if (p == WR || p == BR) + { + dirs = rd; + ndirs = 4; + } + else + { // queen + // iterate both sets + for (int i = 0; i < 4; i++) + { + int to = sq + bd[i]; + while (on_board(to)) + { + Piece tp = pos->board[to]; + if (tp == EMPTY) + { + if (!captures_only) + { + count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); + } + } + else + { + if (color_of(tp) != us) + { + count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0); + } + break; + } + to += bd[i]; + } + } + for (int i = 0; i < 4; i++) + { + int to = sq + rd[i]; + while (on_board(to)) + { + Piece tp = pos->board[to]; + if (tp == EMPTY) + { + if (!captures_only) + { + count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); + } + } + else + { + if (color_of(tp) != us) + { + count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0); + } + break; + } + to += rd[i]; + } + } + break; + } + for (int i = 0; i < ndirs; i++) + { + int to = sq + dirs[i]; + while (on_board(to)) + { + Piece tp = pos->board[to]; + if (tp == EMPTY) + { + if (!captures_only) + { + count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); + } + } + else + { + if (color_of(tp) != us) + { count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0); } + break; + } + to += dirs[i]; + } + } + } + break; + case WK: + case BK: + { + static const int kd[8] = {1, -1, 16, -16, 17, 15, -17, -15}; + for (int i = 0; i < 8; i++) + { + int to = sq + kd[i]; + if (!on_board(to)) + { + continue; + } + Piece tp = pos->board[to]; + if (tp == EMPTY) + { + if (!captures_only) + { + count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); } } - // en-passant - if (pos->ep_square>=0){ - for (int i=0;i<2;i++){ - int to = caps[i]; if (!on_board(to)) continue; - if (to==pos->ep_square){ - count = add_move(moves, count, max_moves, sq, to, 1, 0, 1, 0); + else if (color_of(tp) != us) + { + count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0); + } + } + // castling (very basic, no check-through validation here; filter later) + if (!captures_only) + { + // Only if not currently in check and path squares are not attacked + Color them = (us == WHITE) ? BLACK : WHITE; + if (us == WHITE) + { + if ((pos->castle & (1 << 0)) && pos->board[0x04] == WK && + pos->board[0x05] == EMPTY && pos->board[0x06] == EMPTY) + { + if (!in_check(pos, WHITE) && !square_attacked_by(pos, 0x05, them) && + !square_attacked_by(pos, 0x06, them)) + { + count = add_move(moves, count, max_moves, sq, 0x06, 0, 0, 0, 1); + } + } + if ((pos->castle & (1 << 1)) && pos->board[0x03] == EMPTY && + pos->board[0x02] == EMPTY && pos->board[0x01] == EMPTY) + { + if (!in_check(pos, WHITE) && !square_attacked_by(pos, 0x03, them) && + !square_attacked_by(pos, 0x02, them)) + { + count = add_move(moves, count, max_moves, sq, 0x02, 0, 0, 0, 1); } } } - } break; - case WN: case BN: { - static const int d[8] = {33,31,18,14,-33,-31,-18,-14}; - for (int i=0;i<8;i++){ - int to = sq + d[i]; if (!on_board(to)) continue; Piece tp=pos->board[to]; - if (tp==EMPTY) { if (!captures_only) count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); } - else if (color_of(tp)!=us) count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0); - } - } break; - case WB: case BB: case WR: case BR: case WQ: case BQ: { - static const int bd[4] = {17,15,-17,-15}; - static const int rd[4] = {1,-1,16,-16}; - const int *dirs = NULL; int ndirs=0; - if (p==WB || p==BB) { dirs=bd; ndirs=4; } - else if (p==WR || p==BR) { dirs=rd; ndirs=4; } - else { // queen - // iterate both sets - for (int i=0;i<4;i++){ - int to = sq + bd[i]; - while(on_board(to)){ - Piece tp=pos->board[to]; - if (tp==EMPTY) { if (!captures_only) count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); } - else { if (color_of(tp)!=us) count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0); break; } - to += bd[i]; + else + { + if ((pos->castle & (1 << 2)) && pos->board[0x74] == BK && + pos->board[0x75] == EMPTY && pos->board[0x76] == EMPTY) + { + if (!in_check(pos, BLACK) && !square_attacked_by(pos, 0x75, them) && + !square_attacked_by(pos, 0x76, them)) + { + count = add_move(moves, count, max_moves, sq, 0x76, 0, 0, 0, 1); } } - for (int i=0;i<4;i++){ - int to = sq + rd[i]; - while(on_board(to)){ - Piece tp=pos->board[to]; - if (tp==EMPTY) { if (!captures_only) count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); } - else { if (color_of(tp)!=us) count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0); break; } - to += rd[i]; - } - } - break; - } - for (int i=0;iboard[to]; - if (tp==EMPTY) { if (!captures_only) count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); } - else { if (color_of(tp)!=us) count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0); break; } - to += dirs[i]; - } - } - } break; - case WK: case BK: { - static const int kd[8] = {1,-1,16,-16,17,15,-17,-15}; - for (int i=0;i<8;i++){ - int to = sq + kd[i]; if (!on_board(to)) continue; Piece tp=pos->board[to]; - if (tp==EMPTY) { if (!captures_only) count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); } - else if (color_of(tp)!=us) count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0); - } - // castling (very basic, no check-through validation here; filter later) - if (!captures_only){ - // Only if not currently in check and path squares are not attacked - Color them = (us==WHITE)?BLACK:WHITE; - if (us==WHITE){ - if ((pos->castle & (1<<0)) && pos->board[0x04]==WK && pos->board[0x05]==EMPTY && pos->board[0x06]==EMPTY){ - if (!in_check(pos, WHITE) && !square_attacked_by(pos, 0x05, them) && !square_attacked_by(pos, 0x06, them)) - count = add_move(moves, count, max_moves, sq, 0x06, 0, 0, 0, 1); - } - if ((pos->castle & (1<<1)) && pos->board[0x03]==EMPTY && pos->board[0x02]==EMPTY && pos->board[0x01]==EMPTY){ - if (!in_check(pos, WHITE) && !square_attacked_by(pos, 0x03, them) && !square_attacked_by(pos, 0x02, them)) - count = add_move(moves, count, max_moves, sq, 0x02, 0, 0, 0, 1); - } - } else { - if ((pos->castle & (1<<2)) && pos->board[0x74]==BK && pos->board[0x75]==EMPTY && pos->board[0x76]==EMPTY){ - if (!in_check(pos, BLACK) && !square_attacked_by(pos, 0x75, them) && !square_attacked_by(pos, 0x76, them)) - count = add_move(moves, count, max_moves, sq, 0x76, 0, 0, 0, 1); - } - if ((pos->castle & (1<<3)) && pos->board[0x73]==EMPTY && pos->board[0x72]==EMPTY && pos->board[0x71]==EMPTY){ - if (!in_check(pos, BLACK) && !square_attacked_by(pos, 0x73, them) && !square_attacked_by(pos, 0x72, them)) - count = add_move(moves, count, max_moves, sq, 0x72, 0, 0, 0, 1); + if ((pos->castle & (1 << 3)) && pos->board[0x73] == EMPTY && + pos->board[0x72] == EMPTY && pos->board[0x71] == EMPTY) + { + if (!in_check(pos, BLACK) && !square_attacked_by(pos, 0x73, them) && + !square_attacked_by(pos, 0x72, them)) + { + count = add_move(moves, count, max_moves, sq, 0x72, 0, 0, 0, 1); } } } - } break; - default: break; + } + } + break; + default: + break; } } return count; } -int gen_moves_pseudo(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) +{ return gen_moves_internal(pos, moves, max_moves, captures_only); } -int gen_moves(const Position *pos, Move *moves, int max_moves, int captures_only){ +int gen_moves(const Position *pos, Move *moves, int max_moves, int captures_only) +{ int count = gen_moves_internal(pos, moves, max_moves, captures_only); // Filter illegal moves leaving our king in check - for (int i=0;iside); - if (illegal){ - moves[i] = moves[count-1]; + if (illegal) + { + moves[i] = moves[count - 1]; count--; - } else { + } + else + { i++; } } return count; } -void make_move(Position *pos, const Move *m, Piece *captured_out){ - Piece fromP = pos->board[m->from]; - Piece toP = pos->board[m->to]; +void make_move(Position *pos, const Move *m, Piece *captured_out) +{ + Piece fromP = pos->board[m->from]; + Piece toP = pos->board[m->to]; *captured_out = toP; // en-passant capture - if (m->is_enpassant){ - int cap_sq = (pos->side==WHITE) ? (m->to - 16) : (m->to + 16); - *captured_out = pos->board[cap_sq]; + if (m->is_enpassant) + { + int cap_sq = (pos->side == WHITE) ? (m->to - 16) : (m->to + 16); + *captured_out = pos->board[cap_sq]; pos->board[cap_sq] = EMPTY; } // move piece - pos->board[m->to] = fromP; + pos->board[m->to] = fromP; pos->board[m->from] = EMPTY; // promotion - if (m->promo){ pos->board[m->to] = (Piece)m->promo; } + if (m->promo) + { + pos->board[m->to] = (Piece)m->promo; + } // castling rook move - if (m->is_castle){ - if (fromP==WK && m->to==0x06){ pos->board[0x05]=WR; pos->board[0x07]=EMPTY; } - else if (fromP==WK && m->to==0x02){ pos->board[0x03]=WR; pos->board[0x00]=EMPTY; } - else if (fromP==BK && m->to==0x76){ pos->board[0x75]=BR; pos->board[0x77]=EMPTY; } - else if (fromP==BK && m->to==0x72){ pos->board[0x73]=BR; pos->board[0x70]=EMPTY; } + if (m->is_castle) + { + if (fromP == WK && m->to == 0x06) + { + pos->board[0x05] = WR; + pos->board[0x07] = EMPTY; + } + else if (fromP == WK && m->to == 0x02) + { + pos->board[0x03] = WR; + pos->board[0x00] = EMPTY; + } + else if (fromP == BK && m->to == 0x76) + { + pos->board[0x75] = BR; + pos->board[0x77] = EMPTY; + } + else if (fromP == BK && m->to == 0x72) + { + pos->board[0x73] = BR; + pos->board[0x70] = EMPTY; + } } // update castling rights conservatively - if (fromP==WK){ pos->castle &= ~(1<<0); pos->castle &= ~(1<<1); } - if (fromP==BK){ pos->castle &= ~(1<<2); pos->castle &= ~(1<<3); } - if (m->from==0x00 || m->to==0x00) pos->castle &= ~(1<<1); - if (m->from==0x07 || m->to==0x07) pos->castle &= ~(1<<0); - if (m->from==0x70 || m->to==0x70) pos->castle &= ~(1<<3); - if (m->from==0x77 || m->to==0x77) pos->castle &= ~(1<<2); + if (fromP == WK) + { + pos->castle &= ~(1 << 0); + pos->castle &= ~(1 << 1); + } + if (fromP == BK) + { + pos->castle &= ~(1 << 2); + pos->castle &= ~(1 << 3); + } + if (m->from == 0x00 || m->to == 0x00) + { + pos->castle &= ~(1 << 1); + } + if (m->from == 0x07 || m->to == 0x07) + { + pos->castle &= ~(1 << 0); + } + if (m->from == 0x70 || m->to == 0x70) + { + pos->castle &= ~(1 << 3); + } + if (m->from == 0x77 || m->to == 0x77) + { + pos->castle &= ~(1 << 2); + } // en-passant square pos->ep_square = -1; - if (fromP==WP && (m->to - m->from)==32) pos->ep_square = m->from + 16; - if (fromP==BP && (m->from - m->to)==32) pos->ep_square = m->from - 16; + if (fromP == WP && (m->to - m->from) == 32) + { + pos->ep_square = m->from + 16; + } + if (fromP == BP && (m->from - m->to) == 32) + { + pos->ep_square = m->from - 16; + } // halfmove clock - if (fromP==WP || fromP==BP || m->is_capture) pos->halfmove_clock = 0; else pos->halfmove_clock++; + if (fromP == WP || fromP == BP || m->is_capture) + { + pos->halfmove_clock = 0; + } + else + { + pos->halfmove_clock++; + } // side to move - pos->side = (pos->side==WHITE)?BLACK:WHITE; - if (pos->side==WHITE) pos->fullmove_number++; + pos->side = (pos->side == WHITE) ? BLACK : WHITE; + if (pos->side == WHITE) + { + pos->fullmove_number++; + } } -void unmake_move(Position *pos, const Move *m, Piece captured){ - pos->side = (pos->side==WHITE)?BLACK:WHITE; - if (pos->side==BLACK) pos->fullmove_number--; +void unmake_move(Position *pos, const Move *m, Piece captured) +{ + pos->side = (pos->side == WHITE) ? BLACK : WHITE; + if (pos->side == BLACK) + { + pos->fullmove_number--; + } Piece moved = pos->board[m->to]; // undo castling rook move - if (m->is_castle){ - if (moved==WK && m->to==0x06){ pos->board[0x07]=WR; pos->board[0x05]=EMPTY; } - else if (moved==WK && m->to==0x02){ pos->board[0x00]=WR; pos->board[0x03]=EMPTY; } - else if (moved==BK && m->to==0x76){ pos->board[0x77]=BR; pos->board[0x75]=EMPTY; } - else if (moved==BK && m->to==0x72){ pos->board[0x70]=BR; pos->board[0x73]=EMPTY; } + if (m->is_castle) + { + if (moved == WK && m->to == 0x06) + { + pos->board[0x07] = WR; + pos->board[0x05] = EMPTY; + } + else if (moved == WK && m->to == 0x02) + { + pos->board[0x00] = WR; + pos->board[0x03] = EMPTY; + } + else if (moved == BK && m->to == 0x76) + { + pos->board[0x77] = BR; + pos->board[0x75] = EMPTY; + } + else if (moved == BK && m->to == 0x72) + { + pos->board[0x70] = BR; + pos->board[0x73] = EMPTY; + } } // undo promotion - if (m->promo){ moved = (pos->side==WHITE)?WP:BP; } + if (m->promo) + { + moved = (pos->side == WHITE) ? WP : BP; + } pos->board[m->from] = moved; - if (m->is_enpassant){ - pos->board[m->to] = EMPTY; - int cap_sq = (pos->side==WHITE) ? (m->to - 16) : (m->to + 16); + if (m->is_enpassant) + { + pos->board[m->to] = EMPTY; + int cap_sq = (pos->side == WHITE) ? (m->to - 16) : (m->to + 16); pos->board[cap_sq] = captured; - } else { + } + else + { pos->board[m->to] = captured; } - // Note: We do not restore previous castle/ep/halfmove here (for perft driver we will handle state by copying Position before make_move) - // For correctness in deeper engine, we’d need a move stack with state; perft here uses position copies for make/unmake. - // To keep unmake consistent for our usage (make->unmake on a copy), we keep simple. + // Note: We do not restore previous castle/ep/halfmove here (for perft driver we will handle + // state by copying Position before make_move) For correctness in deeper engine, we’d need a + // move stack with state; perft here uses position copies for make/unmake. To keep unmake + // consistent for our usage (make->unmake on a copy), we keep simple. } -int square_from_algebraic(const char *uci4, int is_from){ +int square_from_algebraic(const char *uci4, int is_from) +{ // uci like e2e4 or e7e8q - if (!uci4 || strlen(uci4) < 4) return -1; - int f = uci4[is_from?0:2] - 'a'; - int r = uci4[is_from?1:3] - '1'; - if (f<0||f>7||r<0||r>7) return -1; - return (r<<4)|f; + if (!uci4 || strlen(uci4) < 4) + { + return -1; + } + int f = uci4[is_from ? 0 : 2] - 'a'; + int r = uci4[is_from ? 1 : 3] - '1'; + if (f < 0 || f > 7 || r < 0 || r > 7) + { + return -1; + } + return (r << 4) | f; } -int move_from_uci(const Position *pos, const char *uci, Move *out){ +int move_from_uci(const Position *pos, const char *uci, Move *out) +{ int from = square_from_algebraic(uci, 1); - int to = square_from_algebraic(uci, 0); - if (from<0||to<0) return 0; - char promo = 0; if (strlen(uci)>=5) promo = uci[4]; + int to = square_from_algebraic(uci, 0); + if (from < 0 || to < 0) + { + return 0; + } + char promo = 0; + if (strlen(uci) >= 5) + { + promo = uci[4]; + } Move moves[256]; - int n = gen_moves(pos, moves, 256, 0); - for (int i=0;iside==WHITE)?WQ:BQ; - else if (promo=='r' || promo=='R') pp = (pos->side==WHITE)?WR:BR; - else if (promo=='b' || promo=='B') pp = (pos->side==WHITE)?WB:BB; - else if (promo=='n' || promo=='N') pp = (pos->side==WHITE)?WN:BN; - if (pp && pp==moves[i].promo){ *out = moves[i]; return 1; } - } else { - if (!promo){ *out = moves[i]; return 1; } + if (promo == 'q' || promo == 'Q') + { + pp = (pos->side == WHITE) ? WQ : BQ; + } + else if (promo == 'r' || promo == 'R') + { + pp = (pos->side == WHITE) ? WR : BR; + } + else if (promo == 'b' || promo == 'B') + { + pp = (pos->side == WHITE) ? WB : BB; + } + else if (promo == 'n' || promo == 'N') + { + pp = (pos->side == WHITE) ? WN : BN; + } + if (pp && pp == moves[i].promo) + { + *out = moves[i]; + return 1; + } + } + else + { + if (!promo) + { + *out = moves[i]; + return 1; + } } } } diff --git a/C/lichess_random_engine/movegen.h b/C/lichess_random_engine/movegen.h index 15efa0f..c60814b 100644 --- a/C/lichess_random_engine/movegen.h +++ b/C/lichess_random_engine/movegen.h @@ -4,46 +4,68 @@ #include // 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); diff --git a/C/lichess_random_engine/perft.c b/C/lichess_random_engine/perft.c index 9869f36..c872f52 100644 --- a/C/lichess_random_engine/perft.c +++ b/C/lichess_random_engine/perft.c @@ -1,66 +1,140 @@ #include "movegen.h" +#include #include -#include #include +#include -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;ifrom & 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= 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= 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 diff --git a/C/lichess_random_engine/search.c b/C/lichess_random_engine/search.c index 168dc90..646817d 100644 --- a/C/lichess_random_engine/search.c +++ b/C/lichess_random_engine/search.c @@ -1,60 +1,112 @@ #include "search.h" +#include "movegen.h" #include #include -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; sqboard[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 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; } diff --git a/C/lichess_random_engine/search.h b/C/lichess_random_engine/search.h index 9ea96d1..b707b4d 100644 --- a/C/lichess_random_engine/search.h +++ b/C/lichess_random_engine/search.h @@ -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 \ No newline at end of file diff --git a/C/misc/generatingWordsEndingWIthalka.c b/C/misc/generatingWordsEndingWIthalka.c new file mode 100644 index 0000000..1a417bb --- /dev/null +++ b/C/misc/generatingWordsEndingWIthalka.c @@ -0,0 +1,12 @@ +#include + +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; +} diff --git a/C/misc/randomJPG/generate_images.c b/C/misc/randomJPG/generate_images.c new file mode 100644 index 0000000..18a5ada --- /dev/null +++ b/C/misc/randomJPG/generate_images.c @@ -0,0 +1,349 @@ +#include +#include +#include +#include +#include +#include +#include + +typedef struct +{ + unsigned char r, g, b; +} RGB; + +void print_usage(const char *program_name) +{ + printf("Usage: %s [options] " + " ... \n", + program_name); + printf("Options:\n"); + printf(" -h, --help Show this help message and exit\n"); + printf("Arguments:\n"); + printf(" Number of images to generate (default: 1)\n"); + printf(" Size of each image (default: 1000)\n"); + printf(" Size of each block (default: 25)\n"); + printf(" Quality of the output image (default: 100)\n"); + printf(" Path to save the output image (default: output.png)\n"); + printf(" Output format (jpeg or bmp, default: jpeg)\n"); + printf(" ... 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; +} diff --git a/C/misc/randomJPG/generate_jpg.c b/C/misc/randomJPG/generate_jpg.c index f1fa51e..86bf139 100644 --- a/C/misc/randomJPG/generate_jpg.c +++ b/C/misc/randomJPG/generate_jpg.c @@ -1,17 +1,21 @@ +#include +#include #include #include #include -#include -#include #include -#include +#include -typedef struct { +typedef struct +{ unsigned char r, g, b; } RGB; -void print_usage(const char* program_name) { - printf("Usage: %s [options] ... \n", program_name); +void print_usage(const char *program_name) +{ + printf("Usage: %s [options] " + "... \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(" ... 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; } diff --git a/C/misc/split/main.c b/C/misc/split/main.c new file mode 100644 index 0000000..634651d --- /dev/null +++ b/C/misc/split/main.c @@ -0,0 +1,115 @@ +#include +#include + +// 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; +} diff --git a/C/opening_learner/chess.c b/C/opening_learner/chess.c index f5e0ed2..6859475 100644 --- a/C/opening_learner/chess.c +++ b/C/opening_learner/chess.c @@ -1,296 +1,670 @@ #include "chess.h" -#include -#include #include +#include #include +#include 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;ipromo = 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; } diff --git a/C/opening_learner/chess.h b/C/opening_learner/chess.h index 9c2c9f6..840802e 100644 --- a/C/opening_learner/chess.h +++ b/C/opening_learner/chess.h @@ -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]); diff --git a/C/opening_learner/engine.c b/C/opening_learner/engine.c index c4ce1b0..0fad02b 100644 --- a/C/opening_learner/engine.c +++ b/C/opening_learner/engine.c @@ -1,139 +1,277 @@ #include "engine.h" -#include +#include #include -#include +#include #include #include +#include #include #include -#include +#include +#include -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 " - 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 (icount) 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 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"); } } diff --git a/C/opening_learner/engine.h b/C/opening_learner/engine.h index 092918f..92e8cf4 100644 --- a/C/opening_learner/engine.h +++ b/C/opening_learner/engine.h @@ -1,20 +1,22 @@ #ifndef ENGINE_H #define ENGINE_H -#include #include +#include #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; diff --git a/C/opening_learner/gui.c b/C/opening_learner/gui.c index 428315d..b3c50aa 100644 --- a/C/opening_learner/gui.c +++ b/C/opening_learner/gui.c @@ -2,156 +2,229 @@ #include #include -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= '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; diff --git a/C/opening_learner/gui.h b/C/opening_learner/gui.h index c409657..f3001ca 100644 --- a/C/opening_learner/gui.h +++ b/C/opening_learner/gui.h @@ -1,20 +1,22 @@ #ifndef GUI_H #define GUI_H -#include #include +#include -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; diff --git a/C/opening_learner/main.c b/C/opening_learner/main.c index 993f89e..14c6d40 100644 --- a/C/opening_learner/main.c +++ b/C/opening_learner/main.c @@ -1,193 +1,283 @@ +#include +#include #include #include -#include #include -#include -#include +#include -#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;i0)? (rand()%wsum) : 0; - size_t pick=0; for (size_t i=0;i0 && 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 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; } diff --git a/C/opening_learner/mistakes.c b/C/opening_learner/mistakes.c index 89f76b0..8a51d9f 100644 --- a/C/opening_learner/mistakes.c +++ b/C/opening_learner/mistakes.c @@ -1,65 +1,102 @@ #include "mistakes.h" +#include #include #include -#include -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;icount;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; } diff --git a/C/opening_learner/mistakes.h b/C/opening_learner/mistakes.h index 7f77ce5..5d97d0e 100644 --- a/C/opening_learner/mistakes.h +++ b/C/opening_learner/mistakes.h @@ -1,27 +1,36 @@ #ifndef MISTAKES_H #define MISTAKES_H -#include #include +#include // 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); diff --git a/C/scrapeWebsite/scrape.c b/C/scrapeWebsite/scrape.c new file mode 100644 index 0000000..b9b18c1 --- /dev/null +++ b/C/scrapeWebsite/scrape.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// 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; +} diff --git a/C/tests/generatingPolishLettersOnWindowsTerminal.c b/C/tests/generatingPolishLettersOnWindowsTerminal.c new file mode 100644 index 0000000..a85765c --- /dev/null +++ b/C/tests/generatingPolishLettersOnWindowsTerminal.c @@ -0,0 +1,10 @@ +#include +#include +#include + +int main() +{ + printf("Henlo\n"); + sleep(20); + return 0; +} diff --git a/C/websocketServer/main.c b/C/websocketServer/main.c index 654a9cd..8200bdb 100644 --- a/C/websocketServer/main.c +++ b/C/websocketServer/main.c @@ -1,68 +1,74 @@ #include -#include #include +#include -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; } -