@@ -20,11 +20,37 @@ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
2020#define SPRITE_W 7
2121#define SPRITE_H 7
2222#define NUM_FRAMES 2
23+ #define NUM_GHOSTS 2
24+ #define SPACING 9
2325#define DEFAULT_SPEED 4
2426#define DEFAULT_ANIM_SPEED 1
2527
26- // Pac-Man: 2 frames (mouth open / mouth closed)
27- static const uint8_t pacman [NUM_FRAMES ][SPRITE_H ] = {
28+ // Pac-Man facing left: 2 frames
29+ static const uint8_t pacman_left [NUM_FRAMES ][SPRITE_H ] = {
30+ // Frame 0 - mouth open
31+ {
32+ 0x1C , // ..###..
33+ 0x3E , // .#####.
34+ 0x1F , // ..#####
35+ 0x07 , // ....###
36+ 0x1F , // ..#####
37+ 0x3E , // .#####.
38+ 0x1C , // ..###..
39+ },
40+ // Frame 1 - mouth closing
41+ {
42+ 0x1C , // ..###..
43+ 0x3E , // .#####.
44+ 0x3F , // .######
45+ 0x7F , // #######
46+ 0x3F , // .######
47+ 0x3E , // .#####.
48+ 0x1C , // ..###..
49+ },
50+ };
51+
52+ // Pac-Man facing right: 2 frames
53+ static const uint8_t pacman_right [NUM_FRAMES ][SPRITE_H ] = {
2854 // Frame 0 - mouth open
2955 {
3056 0x1C , // ..###..
@@ -47,9 +73,8 @@ static const uint8_t pacman[NUM_FRAMES][SPRITE_H] = {
4773 },
4874};
4975
50- // Ghost: 2 frames (legs alternate)
51- static const uint8_t ghost [NUM_FRAMES ][SPRITE_H ] = {
52- // Frame 0
76+ // Normal ghost: 2 frames (legs alternate)
77+ static const uint8_t ghost_normal [NUM_FRAMES ][SPRITE_H ] = {
5378 {
5479 0x1C , // ..###..
5580 0x3E , // .#####.
@@ -59,7 +84,6 @@ static const uint8_t ghost[NUM_FRAMES][SPRITE_H] = {
5984 0x7F , // #######
6085 0x55 , // #.#.#.#
6186 },
62- // Frame 1
6387 {
6488 0x1C , // ..###..
6589 0x3E , // .#####.
@@ -71,23 +95,39 @@ static const uint8_t ghost[NUM_FRAMES][SPRITE_H] = {
7195 },
7296};
7397
74- // Dot: small pellet centered in a 3-wide space (only middle row has a pixel)
75- #define DOT_W 3
76- #define DOT_H 7
77-
78- // Pattern layout:
79- // [pacman 7] [gap 2] [dot 3] [dot 3] [dot 3] [ghost 7] [gap 2] [ghost 7] [gap 2]
80- // Total: 7 + 2 + 3 + 3 + 3 + 7 + 2 + 7 + 2 = 36
98+ // Scared ghost (blue): 2 frames
99+ static const uint8_t ghost_scared [NUM_FRAMES ][SPRITE_H ] = {
100+ {
101+ 0x1C , // ..###..
102+ 0x3E , // .#####.
103+ 0x55 , // #.#.#.#
104+ 0x7F , // #######
105+ 0x6B , // ##.#.##
106+ 0x7F , // #######
107+ 0x55 , // #.#.#.#
108+ },
109+ {
110+ 0x1C , // ..###..
111+ 0x3E , // .#####.
112+ 0x55 , // #.#.#.#
113+ 0x7F , // #######
114+ 0x6B , // ##.#.##
115+ 0x7F , // #######
116+ 0x2A , // .#.#.#.
117+ },
118+ };
81119
82- #define NUM_ELEMENTS 9
83- static const int element_widths [NUM_ELEMENTS ] = { 7 , 2 , 3 , 3 , 3 , 7 , 2 , 7 , 2 };
84- // Element types: 0=pacman, 1=gap, 2=dot, 3=ghost
85- static const int element_types [NUM_ELEMENTS ] = { 0 , 1 , 2 , 2 , 2 , 3 , 1 , 3 , 1 };
86- #define TOTAL_PATTERN_W 36
120+ // Phase 0: ghosts chase pac-man, all moving left
121+ // positions: pacman at x, ghost0 at x+SPACING, ghost1 at x+2*SPACING
122+ // start: x = grid->cols, end: x + 2*SPACING + SPRITE_W < 0
123+ //
124+ // Phase 1: pac-man chases scared ghosts, all moving right
125+ // positions: ghost0 at x, ghost1 at x+SPACING, pacman at x+2*SPACING
126+ // start: x = -total_width, end: x > grid->cols
87127
88128typedef struct {
89- int offset ;
90- int dir ;
129+ int x ;
130+ int phase ;
91131 int tick ;
92132 int speed ;
93133 int anim_tick ;
@@ -96,42 +136,50 @@ typedef struct {
96136 bool colorful ;
97137 Color color ;
98138 Color pacman_color ;
99- Color ghost_colors [2 ];
100- Color dot_color ;
139+ Color ghost_colors [NUM_GHOSTS ];
140+ Color scared_color ;
141+ int cols ;
101142} PacmanMarchData ;
102143
103144static void PrintHelp () {
104145 printf (" -C Enable colored mode\n" );
105- printf (" -R Reverse direction (scroll right)\n" );
106146 printf (" -S <ticks> Scroll speed, higher = slower (default: %d)\n" , DEFAULT_SPEED );
107147 printf (" -N <ticks> Animation speed, higher = slower (default: %d)\n" , DEFAULT_ANIM_SPEED );
108148 printf (" -P <color> Pac-Man color (R,G,B, default: 255,255,85)\n" );
109149 printf (" -G <color> Ghost 1 color (R,G,B, default: 255,85,85)\n" );
110- printf (" -K <color> Ghost 2 color (R,G,B, default: 85,255,255)\n" );
111- printf (" -E <color> Dot color (R,G,B, default: 255,185,80)\n" );
150+ printf (" -K <color> Ghost 2 color (R,G,B, default: 255,85,255)\n" );
151+ printf (" -E <color> Scared ghost color (R,G,B, default: 85,85,255)\n" );
152+ }
153+
154+ static void StartPhase (PacmanMarchData * pd , int phase ) {
155+ pd -> phase = phase ;
156+ int total = 2 * SPACING + SPRITE_W ;
157+ if (phase == 0 ) {
158+ pd -> x = pd -> cols ;
159+ } else {
160+ pd -> x = - total ;
161+ }
112162}
113163
114164static void * Create (Grid * grid , int argc , char * argv []) {
115- (void )grid ;
116165 PacmanMarchData * pd = calloc (1 , sizeof (PacmanMarchData ));
117166 if (!pd ) return NULL ;
118167
119168 pd -> color = GREEN ;
120- pd -> dir = 1 ;
121169 pd -> speed = DEFAULT_SPEED ;
122170 pd -> anim_speed = DEFAULT_ANIM_SPEED ;
123171 pd -> colorful = false;
124172 pd -> pacman_color = (Color ){255 , 255 , 85 , 255 };
125173 pd -> ghost_colors [0 ] = (Color ){255 , 85 , 85 , 255 };
126- pd -> ghost_colors [1 ] = (Color ){85 , 255 , 255 , 255 };
127- pd -> dot_color = (Color ){255 , 185 , 80 , 255 };
174+ pd -> ghost_colors [1 ] = (Color ){255 , 85 , 255 , 255 };
175+ pd -> scared_color = (Color ){85 , 85 , 255 , 255 };
176+ pd -> cols = grid -> cols ;
128177
129178 int opt ;
130179 optind = 1 ;
131- while ((opt = getopt (argc , argv , ":d:CRS :N:P:G:K:E:" )) != -1 ) {
180+ while ((opt = getopt (argc , argv , ":d:CS :N:P:G:K:E:" )) != -1 ) {
132181 switch (opt ) {
133182 case 'C' : pd -> colorful = true; break ;
134- case 'R' : pd -> dir = -1 ; break ;
135183 case 'S' :
136184 pd -> speed = atoi (optarg );
137185 if (pd -> speed < 1 ) pd -> speed = 1 ;
@@ -143,60 +191,25 @@ static void *Create(Grid *grid, int argc, char *argv[]) {
143191 case 'P' : pd -> pacman_color = ParseColor (optarg ); break ;
144192 case 'G' : pd -> ghost_colors [0 ] = ParseColor (optarg ); break ;
145193 case 'K' : pd -> ghost_colors [1 ] = ParseColor (optarg ); break ;
146- case 'E' : pd -> dot_color = ParseColor (optarg ); break ;
194+ case 'E' : pd -> scared_color = ParseColor (optarg ); break ;
147195 }
148196 }
149197
198+ StartPhase (pd , 0 );
150199 return pd ;
151200}
152201
153- static void DrawColumn (PacmanMarchData * pd , Grid * grid , int screen_x , int pattern_col ,
154- int start_row , int frame ) {
155- // Find which element this column belongs to
156- int col = pattern_col ;
157- int elem = 0 ;
158- while (elem < NUM_ELEMENTS && col >= element_widths [elem ]) {
159- col -= element_widths [elem ];
160- elem ++ ;
161- }
162- if (elem >= NUM_ELEMENTS ) return ;
163-
164- int type = element_types [elem ];
165-
166- if (type == 1 ) return ; // gap
167-
168- if (type == 2 ) {
169- // Dot: single pixel in the center (col 1, row 3)
170- if (col == 1 ) {
171- int gy = start_row + SPRITE_H / 2 ;
172- if (gy >= 0 && gy < grid -> rows ) {
173- Color c = pd -> colorful ? pd -> dot_color : pd -> color ;
174- GridSetColor (grid , (Pos ){screen_x , gy }, c );
202+ static void DrawSprite (Grid * grid , const uint8_t * sprite , int sx , int start_row , Color color ) {
203+ for (int c = 0 ; c < SPRITE_W ; c ++ ) {
204+ int gx = sx + c ;
205+ if (gx < 0 || gx >= grid -> cols ) continue ;
206+ for (int r = 0 ; r < SPRITE_H ; r ++ ) {
207+ int gy = start_row + r ;
208+ if (gy < 0 || gy >= grid -> rows ) continue ;
209+ if ((sprite [r ] >> (SPRITE_W - 1 - c )) & 1 ) {
210+ GridSetColor (grid , (Pos ){gx , gy }, color );
175211 }
176212 }
177- return ;
178- }
179-
180- const uint8_t * sprite ;
181- Color c ;
182-
183- if (type == 0 ) {
184- // Pac-Man
185- sprite = pacman [frame ];
186- c = pd -> colorful ? pd -> pacman_color : pd -> color ;
187- } else {
188- // Ghost - figure out which one (first or second ghost in pattern)
189- int ghost_idx = (elem == 5 ) ? 0 : 1 ;
190- sprite = ghost [frame ];
191- c = pd -> colorful ? pd -> ghost_colors [ghost_idx ] : pd -> color ;
192- }
193-
194- for (int r = 0 ; r < SPRITE_H ; r ++ ) {
195- int gy = start_row + r ;
196- if (gy < 0 || gy >= grid -> rows ) continue ;
197- if ((sprite [r ] >> (SPRITE_W - 1 - col )) & 1 ) {
198- GridSetColor (grid , (Pos ){screen_x , gy }, c );
199- }
200213 }
201214}
202215
@@ -207,18 +220,47 @@ static void UpdateFrame(Grid *grid, void *data) {
207220 GridFillColor (grid , bg );
208221
209222 int start_row = (grid -> rows - SPRITE_H ) / 2 ;
210-
211- for (int x = 0 ; x < grid -> cols ; x ++ ) {
212- int pcol = ((x + pd -> offset ) % TOTAL_PATTERN_W + TOTAL_PATTERN_W ) % TOTAL_PATTERN_W ;
213- DrawColumn (pd , grid , x , pcol , start_row , pd -> frame );
223+ int f = pd -> frame ;
224+
225+ if (pd -> phase == 0 ) {
226+ // Ghosts chase Pac-Man, all moving left
227+ // Pac-Man leads (leftmost), ghosts follow
228+ Color pc = pd -> colorful ? pd -> pacman_color : pd -> color ;
229+ DrawSprite (grid , pacman_left [f ], pd -> x , start_row , pc );
230+ for (int i = 0 ; i < NUM_GHOSTS ; i ++ ) {
231+ Color gc = pd -> colorful ? pd -> ghost_colors [i ] : pd -> color ;
232+ DrawSprite (grid , ghost_normal [f ], pd -> x + (i + 1 ) * SPACING , start_row , gc );
233+ }
234+ } else {
235+ // Pac-Man chases scared ghosts, all moving right
236+ // Ghosts lead (leftmost), Pac-Man follows
237+ for (int i = 0 ; i < NUM_GHOSTS ; i ++ ) {
238+ Color sc = pd -> colorful ? pd -> scared_color : pd -> color ;
239+ DrawSprite (grid , ghost_scared [f ], pd -> x + i * SPACING , start_row , sc );
240+ }
241+ Color pc = pd -> colorful ? pd -> pacman_color : pd -> color ;
242+ DrawSprite (grid , pacman_right [f ], pd -> x + NUM_GHOSTS * SPACING , start_row , pc );
214243 }
215244
245+ // Scroll
216246 pd -> tick ++ ;
217247 if (pd -> tick >= pd -> speed ) {
218248 pd -> tick = 0 ;
219- pd -> offset += pd -> dir ;
249+ int total = 2 * SPACING + SPRITE_W ;
250+ if (pd -> phase == 0 ) {
251+ pd -> x -- ;
252+ if (pd -> x + total < 0 ) {
253+ StartPhase (pd , 1 );
254+ }
255+ } else {
256+ pd -> x ++ ;
257+ if (pd -> x > pd -> cols ) {
258+ StartPhase (pd , 0 );
259+ }
260+ }
220261 }
221262
263+ // Animation
222264 pd -> anim_tick ++ ;
223265 if (pd -> anim_tick >= pd -> anim_speed ) {
224266 pd -> anim_tick = 0 ;
0 commit comments