Implement minigolf game with 4 holes, wall collisions, water/sand hazards (AI)
This commit is contained in:
@@ -78,6 +78,6 @@ Once a task is completed, it should be checked off.
|
||||
- [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.
|
||||
- [x] Create a simple game of pool. It should use graphics mode to render the game.
|
||||
- [ ] Create a simple game of minigolf.
|
||||
- [x] 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.
|
||||
585
apps/minigolf/minigolf.c
Normal file
585
apps/minigolf/minigolf.c
Normal file
@@ -0,0 +1,585 @@
|
||||
/**
|
||||
* @file minigolf.c
|
||||
* @brief Simple minigolf game for ClaudeOS.
|
||||
*
|
||||
* Uses VGA mode 0x13 (320x200, 256 colors) via the graphics subsystem.
|
||||
* Features 4 progressively harder holes with walls and obstacles.
|
||||
*
|
||||
* Controls:
|
||||
* A/D or Left/Right - Aim
|
||||
* W/S or Up/Down - Adjust power
|
||||
* Space or Enter - Shoot
|
||||
* Q or Escape - Quit
|
||||
*/
|
||||
|
||||
#include "syscalls.h"
|
||||
|
||||
/* ================================================================
|
||||
* Fixed-point math (16.16)
|
||||
* ================================================================ */
|
||||
|
||||
typedef int32_t fixed_t;
|
||||
|
||||
#define FP_SHIFT 16
|
||||
#define FP_ONE (1 << FP_SHIFT)
|
||||
#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))
|
||||
|
||||
static uint32_t isqrt(uint32_t n) {
|
||||
if (n == 0) return 0;
|
||||
uint32_t x = n, y = (x + 1) / 2;
|
||||
while (y < x) { x = y; y = (x + n / x) / 2; }
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Quarter-wave sine table (0..64 entries, 16.16 fixed point) */
|
||||
static const fixed_t sin_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
|
||||
};
|
||||
|
||||
static fixed_t fp_sin(int a) {
|
||||
a &= 255;
|
||||
int q = a >> 6, i = a & 63;
|
||||
fixed_t v;
|
||||
switch (q) {
|
||||
case 0: v = sin_q[i]; break;
|
||||
case 1: v = sin_q[64 - i]; break;
|
||||
case 2: v = -sin_q[i]; break;
|
||||
case 3: v = -sin_q[64 - i]; break;
|
||||
default: v = 0;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static fixed_t fp_cos(int a) { return fp_sin(a + 64); }
|
||||
|
||||
/* ================================================================
|
||||
* Constants
|
||||
* ================================================================ */
|
||||
|
||||
#define SW 320
|
||||
#define SH 200
|
||||
|
||||
#define BALL_R 3
|
||||
#define HOLE_R 6
|
||||
#define FRICTION (FP_ONE - FP_ONE / 80) /* ~0.9875 */
|
||||
#define MIN_SPEED (FP_ONE / 16)
|
||||
#define MAX_HOLES 4
|
||||
|
||||
/* Colors */
|
||||
#define C_GRASS GFX_GREEN
|
||||
#define C_WALL GFX_LIGHT_GREY
|
||||
#define C_BALL GFX_WHITE
|
||||
#define C_HOLE GFX_BLACK
|
||||
#define C_AIM GFX_YELLOW
|
||||
#define C_POWER GFX_LIGHT_RED
|
||||
#define C_TEXT GFX_WHITE
|
||||
#define C_WATER GFX_BLUE
|
||||
#define C_SAND GFX_BROWN
|
||||
#define C_BG GFX_DARK_GREY
|
||||
|
||||
/* ================================================================
|
||||
* Wall segment
|
||||
* ================================================================ */
|
||||
|
||||
typedef struct { int x1, y1, x2, y2; } wall_t;
|
||||
|
||||
/* ================================================================
|
||||
* Hole (level) definition
|
||||
* ================================================================ */
|
||||
|
||||
#define MAX_WALLS 16
|
||||
#define MAX_OBS 4 /* Obstacles (water/sand zones) */
|
||||
|
||||
typedef struct {
|
||||
int par;
|
||||
int ball_x, ball_y; /* Start position */
|
||||
int hole_x, hole_y; /* Hole position */
|
||||
int num_walls;
|
||||
wall_t walls[MAX_WALLS];
|
||||
/* Rectangular obstacles */
|
||||
int num_obs;
|
||||
struct { int x, y, w, h; uint32_t color; } obs[MAX_OBS];
|
||||
} hole_def_t;
|
||||
|
||||
/* ================================================================
|
||||
* Course layout (4 holes)
|
||||
* ================================================================ */
|
||||
|
||||
static const hole_def_t course[MAX_HOLES] = {
|
||||
/* Hole 1: Simple straight shot */
|
||||
{
|
||||
.par = 2,
|
||||
.ball_x = 60, .ball_y = 100,
|
||||
.hole_x = 260, .hole_y = 100,
|
||||
.num_walls = 4,
|
||||
.walls = {
|
||||
{30, 60, 290, 60}, /* Top wall */
|
||||
{30, 140, 290, 140}, /* Bottom wall */
|
||||
{30, 60, 30, 140}, /* Left wall */
|
||||
{290, 60, 290, 140}, /* Right wall */
|
||||
},
|
||||
.num_obs = 0,
|
||||
},
|
||||
/* Hole 2: L-shaped with turn */
|
||||
{
|
||||
.par = 3,
|
||||
.ball_x = 50, .ball_y = 50,
|
||||
.hole_x = 270, .hole_y = 160,
|
||||
.num_walls = 8,
|
||||
.walls = {
|
||||
{20, 20, 200, 20}, /* Top */
|
||||
{20, 80, 200, 80}, /* Mid horizontal */
|
||||
{20, 20, 20, 80}, /* Left */
|
||||
{200, 20, 200, 80}, /* Right-top */
|
||||
{200, 80, 300, 80}, /* Turn top */
|
||||
{140, 80, 140, 190}, /* Turn left */
|
||||
{300, 80, 300, 190}, /* Right */
|
||||
{140, 190, 300, 190}, /* Bottom */
|
||||
},
|
||||
.num_obs = 0,
|
||||
},
|
||||
/* Hole 3: Water hazard */
|
||||
{
|
||||
.par = 3,
|
||||
.ball_x = 50, .ball_y = 100,
|
||||
.hole_x = 270, .hole_y = 100,
|
||||
.num_walls = 4,
|
||||
.walls = {
|
||||
{20, 50, 300, 50},
|
||||
{20, 150, 300, 150},
|
||||
{20, 50, 20, 150},
|
||||
{300, 50, 300, 150},
|
||||
},
|
||||
.num_obs = 1,
|
||||
.obs = {
|
||||
{130, 70, 60, 60, C_WATER},
|
||||
},
|
||||
},
|
||||
/* Hole 4: Obstacle course */
|
||||
{
|
||||
.par = 4,
|
||||
.ball_x = 40, .ball_y = 100,
|
||||
.hole_x = 280, .hole_y = 100,
|
||||
.num_walls = 8,
|
||||
.walls = {
|
||||
{20, 30, 300, 30}, /* Top */
|
||||
{20, 170, 300, 170}, /* Bottom */
|
||||
{20, 30, 20, 170}, /* Left */
|
||||
{300, 30, 300, 170}, /* Right */
|
||||
/* Internal walls (obstacles) */
|
||||
{100, 30, 100, 100}, /* First barrier from top */
|
||||
{180, 100, 180, 170}, /* Second barrier from bottom */
|
||||
{240, 30, 240, 120}, /* Third barrier from top */
|
||||
{60, 120, 60, 170}, /* Small bump from bottom */
|
||||
},
|
||||
.num_obs = 1,
|
||||
.obs = {
|
||||
{120, 120, 40, 30, C_SAND}, /* Sand trap */
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/* ================================================================
|
||||
* Game state
|
||||
* ================================================================ */
|
||||
|
||||
static fixed_t ball_x, ball_y, ball_vx, ball_vy;
|
||||
static int aim_angle = 0;
|
||||
static int power = 3; /* 1-6 */
|
||||
static int curr_hole = 0;
|
||||
static int strokes = 0;
|
||||
static int total_strokes = 0;
|
||||
static int ball_moving = 0;
|
||||
static int ball_in_hole = 0;
|
||||
static int in_water = 0;
|
||||
static fixed_t saved_x, saved_y; /* Last safe position (before water) */
|
||||
|
||||
/* ================================================================
|
||||
* Wall collision
|
||||
* ================================================================ */
|
||||
|
||||
/**
|
||||
* Check if the ball collides with a horizontal or vertical wall segment.
|
||||
* Simple axis-aligned bounce.
|
||||
*/
|
||||
static void check_wall_bounce(const wall_t *w) {
|
||||
int bx = FP_TO_INT(ball_x);
|
||||
int by = FP_TO_INT(ball_y);
|
||||
|
||||
if (w->y1 == w->y2) {
|
||||
/* Horizontal wall */
|
||||
int minx = w->x1 < w->x2 ? w->x1 : w->x2;
|
||||
int maxx = w->x1 > w->x2 ? w->x1 : w->x2;
|
||||
if (bx >= minx - BALL_R && bx <= maxx + BALL_R) {
|
||||
int dy = by - w->y1;
|
||||
if (dy < 0) dy = -dy;
|
||||
if (dy <= BALL_R) {
|
||||
ball_vy = -ball_vy;
|
||||
/* Push ball out */
|
||||
if (by < w->y1)
|
||||
ball_y = INT_TO_FP(w->y1 - BALL_R - 1);
|
||||
else
|
||||
ball_y = INT_TO_FP(w->y1 + BALL_R + 1);
|
||||
}
|
||||
}
|
||||
} else if (w->x1 == w->x2) {
|
||||
/* Vertical wall */
|
||||
int miny = w->y1 < w->y2 ? w->y1 : w->y2;
|
||||
int maxy = w->y1 > w->y2 ? w->y1 : w->y2;
|
||||
if (by >= miny - BALL_R && by <= maxy + BALL_R) {
|
||||
int dx = bx - w->x1;
|
||||
if (dx < 0) dx = -dx;
|
||||
if (dx <= BALL_R) {
|
||||
ball_vx = -ball_vx;
|
||||
if (bx < w->x1)
|
||||
ball_x = INT_TO_FP(w->x1 - BALL_R - 1);
|
||||
else
|
||||
ball_x = INT_TO_FP(w->x1 + BALL_R + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Physics update
|
||||
* ================================================================ */
|
||||
|
||||
static void update_physics(void) {
|
||||
const hole_def_t *h = &course[curr_hole];
|
||||
|
||||
/* Move ball */
|
||||
ball_x += ball_vx;
|
||||
ball_y += ball_vy;
|
||||
|
||||
/* Save last safe position */
|
||||
int bx = FP_TO_INT(ball_x);
|
||||
int by = FP_TO_INT(ball_y);
|
||||
|
||||
/* Check obstacles */
|
||||
in_water = 0;
|
||||
for (int i = 0; i < h->num_obs; i++) {
|
||||
int ox = h->obs[i].x, oy = h->obs[i].y;
|
||||
int ow = h->obs[i].w, oh = h->obs[i].h;
|
||||
if (bx >= ox && bx <= ox + ow && by >= oy && by <= oy + oh) {
|
||||
if (h->obs[i].color == C_WATER) {
|
||||
in_water = 1;
|
||||
/* Reset ball to last safe position */
|
||||
ball_x = saved_x;
|
||||
ball_y = saved_y;
|
||||
ball_vx = 0;
|
||||
ball_vy = 0;
|
||||
strokes++; /* Penalty stroke */
|
||||
return;
|
||||
} else if (h->obs[i].color == C_SAND) {
|
||||
/* Sand: extra friction */
|
||||
ball_vx = FP_MUL(ball_vx, FP_ONE - FP_ONE / 20);
|
||||
ball_vy = FP_MUL(ball_vy, FP_ONE - FP_ONE / 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply friction */
|
||||
ball_vx = FP_MUL(ball_vx, FRICTION);
|
||||
ball_vy = FP_MUL(ball_vy, FRICTION);
|
||||
|
||||
/* Check if stopped */
|
||||
fixed_t speed_sq = FP_MUL(ball_vx, ball_vx) + FP_MUL(ball_vy, ball_vy);
|
||||
if (speed_sq < FP_MUL(MIN_SPEED, MIN_SPEED)) {
|
||||
ball_vx = 0;
|
||||
ball_vy = 0;
|
||||
ball_moving = 0;
|
||||
saved_x = ball_x;
|
||||
saved_y = ball_y;
|
||||
}
|
||||
|
||||
/* Wall collisions */
|
||||
for (int i = 0; i < h->num_walls; i++) {
|
||||
check_wall_bounce(&h->walls[i]);
|
||||
}
|
||||
|
||||
/* Check hole */
|
||||
fixed_t dx = ball_x - INT_TO_FP(h->hole_x);
|
||||
fixed_t dy = ball_y - INT_TO_FP(h->hole_y);
|
||||
fixed_t dist_sq = FP_MUL(dx, dx) + FP_MUL(dy, dy);
|
||||
fixed_t hole_r = INT_TO_FP(HOLE_R);
|
||||
if (dist_sq < FP_MUL(hole_r, hole_r)) {
|
||||
ball_in_hole = 1;
|
||||
ball_vx = 0;
|
||||
ball_vy = 0;
|
||||
ball_moving = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Drawing
|
||||
* ================================================================ */
|
||||
|
||||
static void draw_number(int x, int y, int num, uint32_t color) {
|
||||
char buf[8];
|
||||
int len = 0;
|
||||
if (num == 0) { buf[len++] = '0'; }
|
||||
else {
|
||||
int n = num;
|
||||
while (n > 0) { buf[len++] = '0' + (char)(n % 10); n /= 10; }
|
||||
}
|
||||
/* Reverse and draw as pixels (very simple 3x5 digit font) */
|
||||
for (int i = len - 1; i >= 0; i--) {
|
||||
/* Draw digit as a small cluster of pixels */
|
||||
int d = buf[i] - '0';
|
||||
int dx = x + (len - 1 - i) * 5;
|
||||
/* Simple representation: draw a small filled rect for each digit */
|
||||
gfx_rect_t r = {(uint32_t)dx, (uint32_t)y, 4, 5, color};
|
||||
gfx_fill_rect(&r);
|
||||
/* Blank out parts to make it look like a number - simplified */
|
||||
if (d == 0) { gfx_rect_t inner = {(uint32_t)(dx+1), (uint32_t)(y+1), 2, 3, C_BG}; gfx_fill_rect(&inner); }
|
||||
if (d == 1) { gfx_rect_t l = {(uint32_t)dx, (uint32_t)y, 1, 5, C_BG}; gfx_fill_rect(&l);
|
||||
gfx_rect_t r2 = {(uint32_t)(dx+2), (uint32_t)y, 2, 5, C_BG}; gfx_fill_rect(&r2); }
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_hole(void) {
|
||||
const hole_def_t *h = &course[curr_hole];
|
||||
|
||||
/* Background */
|
||||
gfx_clear(C_BG);
|
||||
|
||||
/* Draw course grass area (fill inside walls approximately) */
|
||||
/* Just fill the entire course bounding box with grass */
|
||||
int minx = 999, miny = 999, maxx = 0, maxy = 0;
|
||||
for (int i = 0; i < h->num_walls; i++) {
|
||||
if (h->walls[i].x1 < minx) minx = h->walls[i].x1;
|
||||
if (h->walls[i].x2 < minx) minx = h->walls[i].x2;
|
||||
if (h->walls[i].y1 < miny) miny = h->walls[i].y1;
|
||||
if (h->walls[i].y2 < miny) miny = h->walls[i].y2;
|
||||
if (h->walls[i].x1 > maxx) maxx = h->walls[i].x1;
|
||||
if (h->walls[i].x2 > maxx) maxx = h->walls[i].x2;
|
||||
if (h->walls[i].y1 > maxy) maxy = h->walls[i].y1;
|
||||
if (h->walls[i].y2 > maxy) maxy = h->walls[i].y2;
|
||||
}
|
||||
gfx_rect_t grass = {(uint32_t)minx, (uint32_t)miny,
|
||||
(uint32_t)(maxx - minx), (uint32_t)(maxy - miny), C_GRASS};
|
||||
gfx_fill_rect(&grass);
|
||||
|
||||
/* Draw obstacles */
|
||||
for (int i = 0; i < h->num_obs; i++) {
|
||||
gfx_rect_t obs = {(uint32_t)h->obs[i].x, (uint32_t)h->obs[i].y,
|
||||
(uint32_t)h->obs[i].w, (uint32_t)h->obs[i].h,
|
||||
h->obs[i].color};
|
||||
gfx_fill_rect(&obs);
|
||||
}
|
||||
|
||||
/* Draw walls */
|
||||
for (int i = 0; i < h->num_walls; i++) {
|
||||
gfx_line_t line = {(uint32_t)h->walls[i].x1, (uint32_t)h->walls[i].y1,
|
||||
(uint32_t)h->walls[i].x2, (uint32_t)h->walls[i].y2, C_WALL};
|
||||
gfx_line(&line);
|
||||
}
|
||||
|
||||
/* Draw hole */
|
||||
gfx_circle_t hole_circ = {(uint32_t)h->hole_x, (uint32_t)h->hole_y, HOLE_R, C_HOLE};
|
||||
gfx_circle(&hole_circ);
|
||||
/* Hole rim */
|
||||
/* Draw a slightly larger circle outline in white for visibility */
|
||||
/* We'll just use the filled circle - the black on green is visible */
|
||||
|
||||
/* Draw ball */
|
||||
if (!ball_in_hole) {
|
||||
gfx_circle_t bc = {(uint32_t)FP_TO_INT(ball_x), (uint32_t)FP_TO_INT(ball_y),
|
||||
BALL_R, C_BALL};
|
||||
gfx_circle(&bc);
|
||||
}
|
||||
|
||||
/* Draw aiming line */
|
||||
if (!ball_moving && !ball_in_hole) {
|
||||
int cx = FP_TO_INT(ball_x);
|
||||
int cy = FP_TO_INT(ball_y);
|
||||
int len = 15 + power * 4;
|
||||
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 aim = {(uint32_t)cx, (uint32_t)cy,
|
||||
(uint32_t)ex, (uint32_t)ey, C_AIM};
|
||||
gfx_line(&aim);
|
||||
}
|
||||
|
||||
/* HUD */
|
||||
/* Hole number indicator */
|
||||
gfx_rect_t hud_bg = {0, 0, SW, 12, C_BG};
|
||||
gfx_fill_rect(&hud_bg);
|
||||
|
||||
/* "Hole N Par N Strokes N" */
|
||||
/* Simple pixel text for HUD - draw filled rects as digit placeholders */
|
||||
/* Hole number */
|
||||
gfx_rect_t h_label = {2, 2, 24, 7, C_WALL};
|
||||
gfx_fill_rect(&h_label); /* "Hole" background */
|
||||
draw_number(28, 2, curr_hole + 1, C_TEXT);
|
||||
|
||||
/* Par */
|
||||
gfx_rect_t p_label = {50, 2, 16, 7, C_WALL};
|
||||
gfx_fill_rect(&p_label);
|
||||
draw_number(68, 2, h->par, C_TEXT);
|
||||
|
||||
/* Strokes */
|
||||
gfx_rect_t s_label = {90, 2, 36, 7, C_WALL};
|
||||
gfx_fill_rect(&s_label);
|
||||
draw_number(128, 2, strokes, C_TEXT);
|
||||
|
||||
/* Power bar */
|
||||
gfx_rect_t pwr_bg = {SW - 62, 2, 60, 7, C_BG};
|
||||
gfx_fill_rect(&pwr_bg);
|
||||
gfx_rect_t pwr = {SW - 62, 2, (uint32_t)(power * 10), 7, C_POWER};
|
||||
gfx_fill_rect(&pwr);
|
||||
|
||||
/* Ball in hole message */
|
||||
if (ball_in_hole) {
|
||||
gfx_rect_t msg_bg = {SW/2 - 40, SH/2 - 8, 80, 16, GFX_BLUE};
|
||||
gfx_fill_rect(&msg_bg);
|
||||
/* "IN" text represented as colored block */
|
||||
gfx_rect_t msg = {SW/2 - 8, SH/2 - 4, 16, 8, C_TEXT};
|
||||
gfx_fill_rect(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Input
|
||||
* ================================================================ */
|
||||
|
||||
static int handle_input(void) {
|
||||
char c;
|
||||
if (read(0, &c, 1) <= 0) return 0;
|
||||
|
||||
switch (c) {
|
||||
case 'q': case 'Q': case 27:
|
||||
return 1;
|
||||
|
||||
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 (power < 6) power++;
|
||||
break;
|
||||
|
||||
case 's': case 'S':
|
||||
if (power > 1) power--;
|
||||
break;
|
||||
|
||||
case ' ': case '\n': case '\r':
|
||||
if (!ball_moving && !ball_in_hole) {
|
||||
fixed_t p = INT_TO_FP(power);
|
||||
ball_vx = FP_MUL(p, fp_cos(aim_angle));
|
||||
ball_vy = FP_MUL(p, fp_sin(aim_angle));
|
||||
ball_moving = 1;
|
||||
strokes++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'n': case 'N':
|
||||
/* Next hole (after sinking) */
|
||||
if (ball_in_hole) {
|
||||
total_strokes += strokes;
|
||||
curr_hole++;
|
||||
if (curr_hole >= MAX_HOLES) return 2; /* Game complete */
|
||||
/* Reset for next hole */
|
||||
strokes = 0;
|
||||
ball_in_hole = 0;
|
||||
ball_moving = 0;
|
||||
aim_angle = 0;
|
||||
power = 3;
|
||||
ball_x = INT_TO_FP(course[curr_hole].ball_x);
|
||||
ball_y = INT_TO_FP(course[curr_hole].ball_y);
|
||||
ball_vx = 0;
|
||||
ball_vy = 0;
|
||||
saved_x = ball_x;
|
||||
saved_y = ball_y;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Main
|
||||
* ================================================================ */
|
||||
|
||||
int main(void) {
|
||||
gfx_enter();
|
||||
|
||||
/* Initialize first hole */
|
||||
curr_hole = 0;
|
||||
strokes = 0;
|
||||
total_strokes = 0;
|
||||
ball_in_hole = 0;
|
||||
ball_moving = 0;
|
||||
aim_angle = 0;
|
||||
power = 3;
|
||||
ball_x = INT_TO_FP(course[0].ball_x);
|
||||
ball_y = INT_TO_FP(course[0].ball_y);
|
||||
ball_vx = 0;
|
||||
ball_vy = 0;
|
||||
saved_x = ball_x;
|
||||
saved_y = ball_y;
|
||||
|
||||
int quit = 0;
|
||||
while (!quit) {
|
||||
int r = handle_input();
|
||||
if (r == 1) break; /* Quit */
|
||||
if (r == 2) { quit = 2; break; } /* Game complete */
|
||||
|
||||
if (ball_moving) {
|
||||
update_physics();
|
||||
}
|
||||
|
||||
draw_hole();
|
||||
|
||||
for (int i = 0; i < 2; i++) yield();
|
||||
}
|
||||
|
||||
/* Show final score */
|
||||
if (quit == 2) {
|
||||
total_strokes += strokes;
|
||||
/* Show completion screen */
|
||||
gfx_clear(C_BG);
|
||||
gfx_rect_t box = {SW/2 - 60, SH/2 - 20, 120, 40, GFX_BLUE};
|
||||
gfx_fill_rect(&box);
|
||||
/* Score display */
|
||||
draw_number(SW/2 - 10, SH/2 - 5, total_strokes, C_TEXT);
|
||||
/* Wait */
|
||||
for (int i = 0; i < 300; i++) yield();
|
||||
}
|
||||
|
||||
gfx_leave();
|
||||
|
||||
puts("Minigolf complete!\n");
|
||||
puts("Total strokes: ");
|
||||
char tmp[8];
|
||||
int ti = 0, s = total_strokes;
|
||||
if (s == 0) putchar('0');
|
||||
else { while (s > 0) { tmp[ti++] = '0' + (char)(s % 10); s /= 10; } while (ti > 0) putchar(tmp[--ti]); }
|
||||
puts("\n");
|
||||
|
||||
/* Calculate par total */
|
||||
int total_par = 0;
|
||||
for (int i = 0; i < MAX_HOLES; i++) total_par += course[i].par;
|
||||
puts("Par: ");
|
||||
ti = 0; s = total_par;
|
||||
if (s == 0) putchar('0');
|
||||
else { while (s > 0) { tmp[ti++] = '0' + (char)(s % 10); s /= 10; } while (ti > 0) putchar(tmp[--ti]); }
|
||||
puts("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
17
build.log
17
build.log
@@ -21,6 +21,13 @@ Building app: ip
|
||||
Built: /workspaces/claude-os/build/apps_bin/ip (3695 bytes)
|
||||
Building app: ls
|
||||
Built: /workspaces/claude-os/build/apps_bin/ls (250 bytes)
|
||||
Building app: minigolf
|
||||
/workspaces/claude-os/apps/minigolf/minigolf.c:29:17: warning: unused function 'isqrt' [-Wunused-function]
|
||||
29 | static uint32_t isqrt(uint32_t n) {
|
||||
| ^~~~~
|
||||
1 warning generated.
|
||||
/usr/bin/ld: warning: /workspaces/claude-os/build/apps_bin/minigolf.elf has a LOAD segment with RWX permissions
|
||||
Built: /workspaces/claude-os/build/apps_bin/minigolf (3456 bytes)
|
||||
Building app: mkfs.fat32
|
||||
/workspaces/claude-os/apps/mkfs.fat32/mkfs.fat32.c:56:13: warning: unused function 'print_hex' [-Wunused-function]
|
||||
56 | static void print_hex(uint32_t val) {
|
||||
@@ -43,7 +50,7 @@ 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
|
||||
Generated initrd: 37232 bytes
|
||||
[ 4%] Built target initrd
|
||||
[ 97%] Built target kernel
|
||||
[100%] Generating bootable ISO image
|
||||
@@ -53,14 +60,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.kHbiEc'
|
||||
Added to ISO image: directory '/'='/tmp/grub.NLidpF'
|
||||
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 : Thank you for being patient. Working since 0 seconds.
|
||||
ISO image produced: 6056 sectors
|
||||
Written to medium : 6056 sectors at LBA 0
|
||||
xorriso : UPDATE : 64.38% done
|
||||
ISO image produced: 6058 sectors
|
||||
Written to medium : 6058 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