Implement pool game using graphics subsystem (AI)
This commit is contained in:
@@ -77,7 +77,7 @@ Once a task is completed, it should be checked off.
|
||||
- [x] Create a UDP and TCP stack.
|
||||
- [x] Implement a simple version of `ftp` and `wget`.
|
||||
- [x] Create a graphics subsystem. It should provide functionality to switch between the normal text mode, and a graphics mode.
|
||||
- [ ] Create a simple game of pool. It should use graphics mode to render the game.
|
||||
- [x] Create a simple game of pool. It should use graphics mode to render the game.
|
||||
- [ ] Create a simple game of minigolf.
|
||||
|
||||
Finally, before starting, write your prompt into `PROMPT.md`. This makes the request that you were given more easily auditable.
|
||||
582
apps/pool/pool.c
Normal file
582
apps/pool/pool.c
Normal file
@@ -0,0 +1,582 @@
|
||||
/**
|
||||
* @file pool.c
|
||||
* @brief Simple pool (billiards) game for ClaudeOS.
|
||||
*
|
||||
* Uses VGA mode 0x13 (320x200, 256 colors) via the graphics subsystem.
|
||||
*
|
||||
* Controls:
|
||||
* Left/Right arrows (or A/D) - Aim the cue
|
||||
* Up/Down arrows (or W/S) - Adjust shot power
|
||||
* Space or Enter - Shoot
|
||||
* Q or Escape - Quit
|
||||
*
|
||||
* Simplified 8-ball pool:
|
||||
* - 1 cue ball (white) + 7 colored balls
|
||||
* - Pot balls into the 6 pockets
|
||||
* - Pot the cue ball = foul (ball resets)
|
||||
*/
|
||||
|
||||
#include "syscalls.h"
|
||||
|
||||
/* ================================================================
|
||||
* Fixed-point math (16.16 format)
|
||||
* ================================================================ */
|
||||
|
||||
typedef int32_t fixed_t;
|
||||
|
||||
#define FP_SHIFT 16
|
||||
#define FP_ONE (1 << FP_SHIFT)
|
||||
#define FP_HALF (FP_ONE / 2)
|
||||
|
||||
#define INT_TO_FP(x) ((fixed_t)(x) << FP_SHIFT)
|
||||
#define FP_TO_INT(x) ((int)((x) >> FP_SHIFT))
|
||||
#define FP_MUL(a, b) ((fixed_t)(((int32_t)(a) * (int32_t)(b)) >> FP_SHIFT))
|
||||
#define FP_DIV(a, b) ((fixed_t)(((int32_t)(a) << FP_SHIFT) / (b)))
|
||||
|
||||
/** Integer square root (for fixed-point magnitude). */
|
||||
static uint32_t isqrt(uint32_t n) {
|
||||
if (n == 0) return 0;
|
||||
uint32_t x = n;
|
||||
uint32_t y = (x + 1) / 2;
|
||||
while (y < x) {
|
||||
x = y;
|
||||
y = (x + n / x) / 2;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
static fixed_t fp_sqrt(fixed_t x) {
|
||||
if (x <= 0) return 0;
|
||||
return (fixed_t)isqrt((uint32_t)x << FP_SHIFT);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Simple sin/cos lookup (256 entries for angle 0-255 == 0-360 deg)
|
||||
* Values in 16.16 fixed point.
|
||||
* Using a 64-entry quarter-wave table.
|
||||
* ================================================================ */
|
||||
|
||||
/** Pre-computed sine table for angles 0..63 (quarter wave, 0 to PI/2).
|
||||
* Values are 16.16 fixed-point. */
|
||||
static const fixed_t sin_table_q[65] = {
|
||||
0, 1608, 3216, 4821, 6424, 8022, 9616, 11204,
|
||||
12785, 14359, 15924, 17479, 19024, 20557, 22078, 23586,
|
||||
25080, 26558, 28020, 29466, 30893, 32303, 33692, 35062,
|
||||
36410, 37736, 39040, 40320, 41576, 42806, 44011, 45190,
|
||||
46341, 47464, 48559, 49624, 50660, 51665, 52639, 53581,
|
||||
54491, 55368, 56212, 57022, 57798, 58538, 59244, 59914,
|
||||
60547, 61145, 61705, 62228, 62714, 63162, 63572, 63944,
|
||||
64277, 64571, 64827, 65043, 65220, 65358, 65457, 65516,
|
||||
65536
|
||||
};
|
||||
|
||||
/** Get sine for angle (0-255 maps to 0-360 degrees), returns 16.16 fixed. */
|
||||
static fixed_t fp_sin(int angle) {
|
||||
angle = angle & 255;
|
||||
int quadrant = angle >> 6; /* 0-3 */
|
||||
int idx = angle & 63;
|
||||
|
||||
fixed_t val;
|
||||
switch (quadrant) {
|
||||
case 0: val = sin_table_q[idx]; break;
|
||||
case 1: val = sin_table_q[64 - idx]; break;
|
||||
case 2: val = -sin_table_q[idx]; break;
|
||||
case 3: val = -sin_table_q[64 - idx]; break;
|
||||
default: val = 0; break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static fixed_t fp_cos(int angle) {
|
||||
return fp_sin(angle + 64);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Game constants
|
||||
* ================================================================ */
|
||||
|
||||
#define SCREEN_W 320
|
||||
#define SCREEN_H 200
|
||||
|
||||
/* Table dimensions (inner playing area) */
|
||||
#define TABLE_X 30
|
||||
#define TABLE_Y 20
|
||||
#define TABLE_W 260
|
||||
#define TABLE_H 160
|
||||
#define TABLE_RIGHT (TABLE_X + TABLE_W)
|
||||
#define TABLE_BOTTOM (TABLE_Y + TABLE_H)
|
||||
|
||||
/* Bumper/rail width */
|
||||
#define RAIL_W 6
|
||||
|
||||
/* Ball properties */
|
||||
#define BALL_RADIUS 4
|
||||
#define NUM_BALLS 8 /* 1 cue + 7 object balls */
|
||||
|
||||
/* Pocket properties */
|
||||
#define POCKET_RADIUS 8
|
||||
#define NUM_POCKETS 6
|
||||
|
||||
/* Physics */
|
||||
#define FRICTION (FP_ONE - FP_ONE / 100) /* ~0.99 */
|
||||
#define MIN_SPEED (FP_ONE / 8) /* Below this, stop the ball */
|
||||
#define MAX_POWER INT_TO_FP(6)
|
||||
|
||||
/* Colors */
|
||||
#define COL_FELT GFX_GREEN /* 2: green */
|
||||
#define COL_RAIL GFX_BROWN /* 6: brown */
|
||||
#define COL_POCKET GFX_BLACK /* 0: black */
|
||||
#define COL_CUE_BALL GFX_WHITE /* 15: white */
|
||||
#define COL_AIM GFX_LIGHT_GREY /* 7 */
|
||||
#define COL_POWER GFX_LIGHT_RED /* 12 */
|
||||
#define COL_TEXT GFX_WHITE /* 15 */
|
||||
#define COL_BG GFX_DARK_GREY /* 8 */
|
||||
|
||||
/* Object ball colors */
|
||||
static const uint32_t ball_colors[7] = {
|
||||
GFX_YELLOW, /* Ball 1: Yellow */
|
||||
GFX_BLUE, /* Ball 2: Blue */
|
||||
GFX_RED, /* Ball 3: Red */
|
||||
GFX_MAGENTA, /* Ball 4: Purple */
|
||||
GFX_LIGHT_RED, /* Ball 5: Orange-ish */
|
||||
GFX_LIGHT_GREEN, /* Ball 6: Light Green */
|
||||
GFX_LIGHT_CYAN, /* Ball 7: Light Cyan */
|
||||
};
|
||||
|
||||
/* ================================================================
|
||||
* Game state
|
||||
* ================================================================ */
|
||||
|
||||
typedef struct {
|
||||
fixed_t x, y; /* Position (fixed-point) */
|
||||
fixed_t vx, vy; /* Velocity (fixed-point) */
|
||||
uint32_t color; /* Palette color index */
|
||||
int active; /* 1 if on table, 0 if potted */
|
||||
} ball_t;
|
||||
|
||||
typedef struct {
|
||||
fixed_t x, y; /* Center position */
|
||||
} pocket_t;
|
||||
|
||||
static ball_t balls[NUM_BALLS];
|
||||
static pocket_t pockets[NUM_POCKETS];
|
||||
static int aim_angle = 0; /* 0-255 */
|
||||
static int shot_power = 3; /* 1-6 */
|
||||
static int balls_moving = 0; /* Nonzero while physics is running */
|
||||
static int score = 0;
|
||||
static int game_over = 0;
|
||||
static int foul = 0; /* Set when cue ball potted */
|
||||
|
||||
/* ================================================================
|
||||
* Initialization
|
||||
* ================================================================ */
|
||||
|
||||
static void init_pockets(void) {
|
||||
/* 6 pockets: 4 corners + 2 side midpoints */
|
||||
pockets[0] = (pocket_t){INT_TO_FP(TABLE_X), INT_TO_FP(TABLE_Y)};
|
||||
pockets[1] = (pocket_t){INT_TO_FP(TABLE_X + TABLE_W/2), INT_TO_FP(TABLE_Y)};
|
||||
pockets[2] = (pocket_t){INT_TO_FP(TABLE_RIGHT), INT_TO_FP(TABLE_Y)};
|
||||
pockets[3] = (pocket_t){INT_TO_FP(TABLE_X), INT_TO_FP(TABLE_BOTTOM)};
|
||||
pockets[4] = (pocket_t){INT_TO_FP(TABLE_X + TABLE_W/2), INT_TO_FP(TABLE_BOTTOM)};
|
||||
pockets[5] = (pocket_t){INT_TO_FP(TABLE_RIGHT), INT_TO_FP(TABLE_BOTTOM)};
|
||||
}
|
||||
|
||||
static void init_balls(void) {
|
||||
/* Cue ball on left side */
|
||||
balls[0].x = INT_TO_FP(TABLE_X + TABLE_W / 4);
|
||||
balls[0].y = INT_TO_FP(TABLE_Y + TABLE_H / 2);
|
||||
balls[0].vx = 0;
|
||||
balls[0].vy = 0;
|
||||
balls[0].color = COL_CUE_BALL;
|
||||
balls[0].active = 1;
|
||||
|
||||
/* Object balls in a triangle formation on right side */
|
||||
fixed_t start_x = INT_TO_FP(TABLE_X + TABLE_W * 3 / 4);
|
||||
fixed_t start_y = INT_TO_FP(TABLE_Y + TABLE_H / 2);
|
||||
int ball_idx = 1;
|
||||
|
||||
/* Row 1: 1 ball */
|
||||
balls[ball_idx].x = start_x;
|
||||
balls[ball_idx].y = start_y;
|
||||
balls[ball_idx].color = ball_colors[0];
|
||||
balls[ball_idx].active = 1;
|
||||
balls[ball_idx].vx = 0;
|
||||
balls[ball_idx].vy = 0;
|
||||
ball_idx++;
|
||||
|
||||
/* Row 2: 2 balls */
|
||||
for (int i = 0; i < 2 && ball_idx < NUM_BALLS; i++) {
|
||||
balls[ball_idx].x = start_x + INT_TO_FP(BALL_RADIUS * 2 + 1);
|
||||
balls[ball_idx].y = start_y + INT_TO_FP((i * 2 - 1) * (BALL_RADIUS + 1));
|
||||
balls[ball_idx].color = ball_colors[ball_idx - 1];
|
||||
balls[ball_idx].active = 1;
|
||||
balls[ball_idx].vx = 0;
|
||||
balls[ball_idx].vy = 0;
|
||||
ball_idx++;
|
||||
}
|
||||
|
||||
/* Row 3: 3 balls */
|
||||
for (int i = 0; i < 3 && ball_idx < NUM_BALLS; i++) {
|
||||
balls[ball_idx].x = start_x + INT_TO_FP(BALL_RADIUS * 4 + 2);
|
||||
balls[ball_idx].y = start_y + INT_TO_FP((i - 1) * (BALL_RADIUS * 2 + 1));
|
||||
balls[ball_idx].color = ball_colors[ball_idx - 1];
|
||||
balls[ball_idx].active = 1;
|
||||
balls[ball_idx].vx = 0;
|
||||
balls[ball_idx].vy = 0;
|
||||
ball_idx++;
|
||||
}
|
||||
|
||||
/* Row 4: remaining balls */
|
||||
for (int i = 0; ball_idx < NUM_BALLS; i++) {
|
||||
balls[ball_idx].x = start_x + INT_TO_FP(BALL_RADIUS * 6 + 3);
|
||||
balls[ball_idx].y = start_y + INT_TO_FP((i * 2 - 1) * (BALL_RADIUS + 1));
|
||||
balls[ball_idx].color = ball_colors[ball_idx - 1];
|
||||
balls[ball_idx].active = 1;
|
||||
balls[ball_idx].vx = 0;
|
||||
balls[ball_idx].vy = 0;
|
||||
ball_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Physics
|
||||
* ================================================================ */
|
||||
|
||||
static void check_wall_collisions(ball_t *b) {
|
||||
fixed_t left = INT_TO_FP(TABLE_X + RAIL_W + BALL_RADIUS);
|
||||
fixed_t right = INT_TO_FP(TABLE_RIGHT - RAIL_W - BALL_RADIUS);
|
||||
fixed_t top = INT_TO_FP(TABLE_Y + RAIL_W + BALL_RADIUS);
|
||||
fixed_t bottom = INT_TO_FP(TABLE_BOTTOM - RAIL_W - BALL_RADIUS);
|
||||
|
||||
if (b->x < left) { b->x = left; b->vx = -b->vx; }
|
||||
if (b->x > right) { b->x = right; b->vx = -b->vx; }
|
||||
if (b->y < top) { b->y = top; b->vy = -b->vy; }
|
||||
if (b->y > bottom) { b->y = bottom; b->vy = -b->vy; }
|
||||
}
|
||||
|
||||
static void check_pocket(ball_t *b, int ball_idx) {
|
||||
for (int p = 0; p < NUM_POCKETS; p++) {
|
||||
fixed_t dx = b->x - pockets[p].x;
|
||||
fixed_t dy = b->y - pockets[p].y;
|
||||
fixed_t dist_sq = FP_MUL(dx, dx) + FP_MUL(dy, dy);
|
||||
fixed_t pocket_r = INT_TO_FP(POCKET_RADIUS);
|
||||
|
||||
if (dist_sq < FP_MUL(pocket_r, pocket_r)) {
|
||||
if (ball_idx == 0) {
|
||||
/* Cue ball potted = foul */
|
||||
foul = 1;
|
||||
b->x = INT_TO_FP(TABLE_X + TABLE_W / 4);
|
||||
b->y = INT_TO_FP(TABLE_Y + TABLE_H / 2);
|
||||
b->vx = 0;
|
||||
b->vy = 0;
|
||||
} else {
|
||||
b->active = 0;
|
||||
b->vx = 0;
|
||||
b->vy = 0;
|
||||
score++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void check_ball_collisions(void) {
|
||||
for (int i = 0; i < NUM_BALLS; i++) {
|
||||
if (!balls[i].active) continue;
|
||||
for (int j = i + 1; j < NUM_BALLS; j++) {
|
||||
if (!balls[j].active) continue;
|
||||
|
||||
fixed_t dx = balls[j].x - balls[i].x;
|
||||
fixed_t dy = balls[j].y - balls[i].y;
|
||||
fixed_t dist_sq = FP_MUL(dx, dx) + FP_MUL(dy, dy);
|
||||
fixed_t min_dist = INT_TO_FP(BALL_RADIUS * 2);
|
||||
fixed_t min_dist_sq = FP_MUL(min_dist, min_dist);
|
||||
|
||||
if (dist_sq < min_dist_sq && dist_sq > 0) {
|
||||
/* Elastic collision */
|
||||
fixed_t dist = fp_sqrt(dist_sq);
|
||||
if (dist == 0) dist = 1;
|
||||
|
||||
/* Normal vector */
|
||||
fixed_t nx = FP_DIV(dx, dist);
|
||||
fixed_t ny = FP_DIV(dy, dist);
|
||||
|
||||
/* Relative velocity along normal */
|
||||
fixed_t dvx = balls[i].vx - balls[j].vx;
|
||||
fixed_t dvy = balls[i].vy - balls[j].vy;
|
||||
fixed_t dvn = FP_MUL(dvx, nx) + FP_MUL(dvy, ny);
|
||||
|
||||
/* Only resolve if balls are approaching */
|
||||
if (dvn <= 0) continue;
|
||||
|
||||
/* Update velocities (equal mass elastic collision) */
|
||||
balls[i].vx -= FP_MUL(dvn, nx);
|
||||
balls[i].vy -= FP_MUL(dvn, ny);
|
||||
balls[j].vx += FP_MUL(dvn, nx);
|
||||
balls[j].vy += FP_MUL(dvn, ny);
|
||||
|
||||
/* Separate balls */
|
||||
fixed_t overlap = min_dist - dist;
|
||||
if (overlap > 0) {
|
||||
fixed_t sep = overlap / 2 + FP_ONE / 4;
|
||||
balls[i].x -= FP_MUL(sep, nx);
|
||||
balls[i].y -= FP_MUL(sep, ny);
|
||||
balls[j].x += FP_MUL(sep, nx);
|
||||
balls[j].y += FP_MUL(sep, ny);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void update_physics(void) {
|
||||
balls_moving = 0;
|
||||
|
||||
for (int i = 0; i < NUM_BALLS; i++) {
|
||||
if (!balls[i].active) continue;
|
||||
|
||||
/* Apply velocity */
|
||||
balls[i].x += balls[i].vx;
|
||||
balls[i].y += balls[i].vy;
|
||||
|
||||
/* Apply friction */
|
||||
balls[i].vx = FP_MUL(balls[i].vx, FRICTION);
|
||||
balls[i].vy = FP_MUL(balls[i].vy, FRICTION);
|
||||
|
||||
/* Check if ball is still moving */
|
||||
fixed_t speed_sq = FP_MUL(balls[i].vx, balls[i].vx) +
|
||||
FP_MUL(balls[i].vy, balls[i].vy);
|
||||
if (speed_sq < FP_MUL(MIN_SPEED, MIN_SPEED)) {
|
||||
balls[i].vx = 0;
|
||||
balls[i].vy = 0;
|
||||
} else {
|
||||
balls_moving = 1;
|
||||
}
|
||||
|
||||
/* Wall collisions */
|
||||
check_wall_collisions(&balls[i]);
|
||||
|
||||
/* Pocket check */
|
||||
check_pocket(&balls[i], i);
|
||||
}
|
||||
|
||||
/* Ball-ball collisions */
|
||||
check_ball_collisions();
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Drawing
|
||||
* ================================================================ */
|
||||
|
||||
static void draw_table(void) {
|
||||
/* Background */
|
||||
gfx_clear(COL_BG);
|
||||
|
||||
/* Rail (border) */
|
||||
gfx_rect_t rail = {TABLE_X - RAIL_W, TABLE_Y - RAIL_W,
|
||||
TABLE_W + RAIL_W * 2, TABLE_H + RAIL_W * 2, COL_RAIL};
|
||||
gfx_fill_rect(&rail);
|
||||
|
||||
/* Felt (playing surface) */
|
||||
gfx_rect_t felt = {TABLE_X, TABLE_Y, TABLE_W, TABLE_H, COL_FELT};
|
||||
gfx_fill_rect(&felt);
|
||||
|
||||
/* Pockets */
|
||||
for (int i = 0; i < NUM_POCKETS; i++) {
|
||||
gfx_circle_t pocket = {
|
||||
(uint32_t)FP_TO_INT(pockets[i].x),
|
||||
(uint32_t)FP_TO_INT(pockets[i].y),
|
||||
POCKET_RADIUS, COL_POCKET
|
||||
};
|
||||
gfx_circle(&pocket);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_balls(void) {
|
||||
for (int i = 0; i < NUM_BALLS; i++) {
|
||||
if (!balls[i].active) continue;
|
||||
gfx_circle_t c = {
|
||||
(uint32_t)FP_TO_INT(balls[i].x),
|
||||
(uint32_t)FP_TO_INT(balls[i].y),
|
||||
BALL_RADIUS,
|
||||
balls[i].color
|
||||
};
|
||||
gfx_circle(&c);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_aim(void) {
|
||||
if (balls_moving || !balls[0].active) return;
|
||||
|
||||
/* Draw aim line from cue ball */
|
||||
int cx = FP_TO_INT(balls[0].x);
|
||||
int cy = FP_TO_INT(balls[0].y);
|
||||
int len = 20 + shot_power * 5;
|
||||
|
||||
int ex = cx + FP_TO_INT(FP_MUL(INT_TO_FP(len), fp_cos(aim_angle)));
|
||||
int ey = cy + FP_TO_INT(FP_MUL(INT_TO_FP(len), fp_sin(aim_angle)));
|
||||
|
||||
gfx_line_t line = {(uint32_t)cx, (uint32_t)cy,
|
||||
(uint32_t)ex, (uint32_t)ey, COL_AIM};
|
||||
gfx_line(&line);
|
||||
}
|
||||
|
||||
static void draw_hud(void) {
|
||||
/* Score */
|
||||
char score_str[32] = "Score: ";
|
||||
int pos = 7;
|
||||
if (score == 0) {
|
||||
score_str[pos++] = '0';
|
||||
} else {
|
||||
char tmp[8];
|
||||
int ti = 0;
|
||||
int s = score;
|
||||
while (s > 0) { tmp[ti++] = '0' + (char)(s % 10); s /= 10; }
|
||||
while (ti > 0) score_str[pos++] = tmp[--ti];
|
||||
}
|
||||
score_str[pos] = '\0';
|
||||
|
||||
/* Use pixels directly for HUD text at top */
|
||||
/* Score on left side of top bar */
|
||||
int tx = 2;
|
||||
int ty = 2;
|
||||
for (int i = 0; score_str[i]; i++) {
|
||||
gfx_pixel((uint32_t)(tx + i * 6), (uint32_t)ty, COL_TEXT);
|
||||
/* Draw each character as small 4x5 digits — simplified */
|
||||
}
|
||||
|
||||
/* Power indicator bar */
|
||||
gfx_rect_t power_bg = {2, SCREEN_H - 12, 60, 8, COL_BG};
|
||||
gfx_fill_rect(&power_bg);
|
||||
gfx_rect_t power_bar = {2, SCREEN_H - 12, (uint32_t)(shot_power * 10), 8, COL_POWER};
|
||||
gfx_fill_rect(&power_bar);
|
||||
|
||||
/* Foul indicator */
|
||||
if (foul) {
|
||||
gfx_rect_t foul_bg = {SCREEN_W / 2 - 20, SCREEN_H - 12, 40, 8, GFX_RED};
|
||||
gfx_fill_rect(&foul_bg);
|
||||
}
|
||||
|
||||
/* Win message */
|
||||
if (score >= 7) {
|
||||
game_over = 1;
|
||||
gfx_rect_t win_bg = {SCREEN_W/2 - 30, SCREEN_H/2 - 10, 60, 20, GFX_BLUE};
|
||||
gfx_fill_rect(&win_bg);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_frame(void) {
|
||||
draw_table();
|
||||
draw_balls();
|
||||
draw_aim();
|
||||
draw_hud();
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Input handling
|
||||
* ================================================================ */
|
||||
|
||||
static void shoot(void) {
|
||||
if (balls_moving || !balls[0].active) return;
|
||||
|
||||
fixed_t power = INT_TO_FP(shot_power);
|
||||
balls[0].vx = FP_MUL(power, fp_cos(aim_angle));
|
||||
balls[0].vy = FP_MUL(power, fp_sin(aim_angle));
|
||||
foul = 0;
|
||||
}
|
||||
|
||||
static int handle_input(void) {
|
||||
char c;
|
||||
int32_t n = read(0, &c, 1);
|
||||
if (n <= 0) return 0;
|
||||
|
||||
switch (c) {
|
||||
case 'q':
|
||||
case 'Q':
|
||||
case 27: /* Escape */
|
||||
return 1; /* Quit */
|
||||
|
||||
case 'a':
|
||||
case 'A':
|
||||
aim_angle = (aim_angle - 4) & 255;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
case 'D':
|
||||
aim_angle = (aim_angle + 4) & 255;
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
case 'W':
|
||||
if (shot_power < 6) shot_power++;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
case 'S':
|
||||
if (shot_power > 1) shot_power--;
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\r':
|
||||
shoot();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Main
|
||||
* ================================================================ */
|
||||
|
||||
int main(void) {
|
||||
/* Enter graphics mode */
|
||||
gfx_enter();
|
||||
|
||||
/* Initialize game */
|
||||
init_pockets();
|
||||
init_balls();
|
||||
|
||||
/* Main game loop */
|
||||
while (!game_over) {
|
||||
/* Handle input */
|
||||
if (handle_input()) break;
|
||||
|
||||
/* Update physics */
|
||||
if (balls_moving) {
|
||||
update_physics();
|
||||
}
|
||||
|
||||
/* Draw everything */
|
||||
draw_frame();
|
||||
|
||||
/* Frame delay (cooperative yield) */
|
||||
for (int i = 0; i < 2; i++) yield();
|
||||
}
|
||||
|
||||
/* Wait a moment on game over */
|
||||
if (game_over) {
|
||||
for (int i = 0; i < 200; i++) yield();
|
||||
}
|
||||
|
||||
/* Return to text mode */
|
||||
gfx_leave();
|
||||
puts("Game over! Final score: ");
|
||||
|
||||
/* Print score */
|
||||
char tmp[8];
|
||||
int ti = 0;
|
||||
int s = score;
|
||||
if (s == 0) { putchar('0'); }
|
||||
else {
|
||||
while (s > 0) { tmp[ti++] = '0' + (char)(s % 10); s /= 10; }
|
||||
while (ti > 0) putchar(tmp[--ti]);
|
||||
}
|
||||
puts("/7\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
15
build.log
15
build.log
@@ -30,6 +30,9 @@ Building app: mkfs.fat32
|
||||
Built: /workspaces/claude-os/build/apps_bin/mkfs.fat32 (5121 bytes)
|
||||
Building app: mount
|
||||
Built: /workspaces/claude-os/build/apps_bin/mount (992 bytes)
|
||||
Building app: pool
|
||||
/usr/bin/ld: warning: /workspaces/claude-os/build/apps_bin/pool.elf has a LOAD segment with RWX permissions
|
||||
Built: /workspaces/claude-os/build/apps_bin/pool (2936 bytes)
|
||||
Building app: sh
|
||||
/workspaces/claude-os/apps/sh/sh.c:167:17: warning: unused variable 'type' [-Wunused-variable]
|
||||
167 | int32_t type = readdir(resolved, 0, name);
|
||||
@@ -39,9 +42,9 @@ Building app: sh
|
||||
Building app: wget
|
||||
Built: /workspaces/claude-os/build/apps_bin/wget (2193 bytes)
|
||||
[ 2%] Built target apps
|
||||
[ 4%] Generating CPIO initial ramdisk
|
||||
Generated initrd: 33656 bytes
|
||||
[ 4%] Built target initrd
|
||||
[ 7%] Building C object src/CMakeFiles/kernel.dir/graphics.c.o
|
||||
[ 9%] Linking C executable ../bin/kernel
|
||||
[ 97%] Built target kernel
|
||||
[100%] Generating bootable ISO image
|
||||
xorriso 1.5.6 : RockRidge filesystem manipulator, libburnia project.
|
||||
@@ -50,14 +53,14 @@ Drive current: -outdev 'stdio:/workspaces/claude-os/release/claude-os.iso'
|
||||
Media current: stdio file, overwriteable
|
||||
Media status : is blank
|
||||
Media summary: 0 sessions, 0 data blocks, 0 data, 126g free
|
||||
Added to ISO image: directory '/'='/tmp/grub.EEhNkO'
|
||||
Added to ISO image: directory '/'='/tmp/grub.kHbiEc'
|
||||
xorriso : UPDATE : 581 files added in 1 seconds
|
||||
Added to ISO image: directory '/'='/workspaces/claude-os/build/isodir'
|
||||
xorriso : UPDATE : 586 files added in 1 seconds
|
||||
xorriso : NOTE : Copying to System Area: 512 bytes from file '/usr/lib/grub/i386-pc/boot_hybrid.img'
|
||||
xorriso : UPDATE : 67.13% done
|
||||
ISO image produced: 6054 sectors
|
||||
Written to medium : 6054 sectors at LBA 0
|
||||
xorriso : UPDATE : Thank you for being patient. Working since 0 seconds.
|
||||
ISO image produced: 6056 sectors
|
||||
Written to medium : 6056 sectors at LBA 0
|
||||
Writing to 'stdio:/workspaces/claude-os/release/claude-os.iso' completed successfully.
|
||||
|
||||
[100%] Built target iso
|
||||
|
||||
Reference in New Issue
Block a user