https://bitbucket.org/hudson/magic-lantern
Tip revision: 989de1b205e0455a30ab8e9d9c1277d67c285b3d authored by a1ex on 22 February 2014, 00:05:50 UTC
Close branch ml-cbr-backend.
Close branch ml-cbr-backend.
Tip revision: 989de1b
sokoban.c
/*
@title Sokoban Game
*
* This is a playable demo for PicoC scripting.
*
* It should help you understand:
* - Basic key processing (wait_key)
* - Simple graphics (draw_rect, fill_rect, draw_circle, put_pixel etc)
* - PicoC programming basics
*
* Enjoy!
*
@param a Level
@range a 1 6
@default a 1
*/
// http://www.sokobano.de/wiki/index.php?title=Sok_format
#define WALL '#'
#define SPACE ' '
#define PLAYER '@'
#define BOX '$'
#define TARGET '.'
#define PLAYER_ON_TARGET '+'
#define BOX_ON_TARGET '*'
// from Wikipedia
char* maze1[9] = {
" ##### ",
"### # ",
"#.@$ # ",
"### $.# ",
"#.##$ # ",
"# # . ##",
"#$ *$$.#",
"# . #",
"########"
};
// Level 1 from http://sneezingtiger.com/sokoban/levels/minicosmosText.html
char* maze2[8] = {
" ##### ",
"### # ",
"# $ # ##",
"# # . #",
"# # #",
"## # #",
" #@ ###",
" ##### "
};
// Level 2 from http://sneezingtiger.com/sokoban/levels/minicosmosText.html
char* maze3[8] = {
" ##### ",
"### # ",
"# $ # ##",
"# # . #",
"# # #",
"##$#. #",
" #@ ###",
" ##### "
};
// Level 23 from http://sneezingtiger.com/sokoban/levels/minicosmosText.html
char* maze4[9] = {
" #### ",
"####### # ",
"# # ",
"# $ #.# # ",
"# $## # ##",
"### @ #",
" ### # #",
" ##. ##",
" ##### "
};
// Level 1 from http://sneezingtiger.com/sokoban/levels/nabokosmosText.html
char* maze5[7] = {
" ##### ",
" # ## ",
"## * .##",
"# $$* #",
"# * . #",
"## @ ###",
" ##### "
};
// Level 2 from http://sneezingtiger.com/sokoban/levels/nabokosmosText.html
char* maze6[11] = {
" #### ",
"### ###",
"# *$ #",
"# # # #",
"# ** #",
"### #@#",
" # ** #",
" # # #",
" # *. #",
" # ###",
" #### "
};
char** maze;
int LINES;
int COLUMNS;
if (a == 1)
{
maze = maze1;
LINES = 9;
COLUMNS = 8;
}
else if (a == 2)
{
maze = maze2;
LINES = 8;
COLUMNS = 8;
}
else if (a == 3)
{
maze = maze3;
LINES = 8;
COLUMNS = 8;
}
else if (a == 4)
{
maze = maze4;
LINES = 9;
COLUMNS = 11;
}
else if (a == 5)
{
maze = maze5;
LINES = 7;
COLUMNS = 8;
}
else if (a == 6)
{
maze = maze6;
LINES = 11;
COLUMNS = 8;
}
char targets[LINES][COLUMNS];
void print_maze()
{
for (int i = 0; i < LINES; i++)
{
for (int j = 0; j < COLUMNS; j++)
{
printf("%c%c", maze[i][j], targets[i][j] ? '*' : ' ');
}
printf("\n");
}
}
void draw_wall(int x, int y, int a)
{
fill_rect(x, y, a, a, COLOR_BLACK);
}
void draw_space(int x, int y, int a, int on_target)
{
fill_rect(x, y, a, a, COLOR_GRAY(80));
if (on_target)
fill_circle(x + a/2, y + a/2, a/4, COLOR_ORANGE);
}
void draw_box(int x, int y, int a, int on_target)
{
int pad = 3;
for (int i = 0; i < pad; i++)
draw_rect(x + i, y + i, a - i*2, a - i*2, COLOR_GRAY(80));
int color = on_target ? COLOR_ORANGE : COLOR_YELLOW;
fill_rect(x + pad, y + pad, a - pad*2, a - pad*2, color);
draw_rect(x + pad, y + pad, a - pad*2, a - pad*2, COLOR_BLACK);
pad++;
draw_rect(x + pad, y + pad, a - pad*2, a - pad*2, COLOR_BLACK);
draw_line(x + pad, y + pad, x + a - pad, y + a - pad, COLOR_BLACK);
draw_line(x + pad - 1, y + pad, x + a - pad - 1, y + a - pad, COLOR_BLACK);
draw_line(x + pad, y + a - pad, x + a - pad, y + pad, COLOR_BLACK);
draw_line(x + pad - 1, y + a - pad, x + a - pad - 1, y + pad, COLOR_BLACK);
}
void draw_player(int x, int y, int a, int on_target)
{
draw_space(x, y, a, 0);
int xc = x + a/2;
int yc = y + a/2;
int r = a * 3/8;
int y_eye = yc - r * 3/8;
int y_mouth = yc + r * 3/8;
fill_circle(xc, yc, r, COLOR_YELLOW);
draw_circle(xc, yc, r, COLOR_BLACK);
draw_circle(xc, yc, r-1, COLOR_BLACK);
fill_circle(xc - r/3, y_eye, r/5, COLOR_BLACK);
fill_circle(xc + r/3, y_eye, r/5, COLOR_BLACK);
int r_mouth = r*2/3;
for (int t = 45; t <= 180-45; t += 2)
{
int xp = xc + r_mouth * cos(t*M_PI/180);
int yp = yc - r/8 + r_mouth * sin(t*M_PI/180);
put_pixel(xp, yp, COLOR_BLACK);
put_pixel(xp, yp+1, COLOR_BLACK);
}
}
void draw_maze()
{
int a = MIN(400 / LINES, 700 / COLUMNS);
int total_w = a * COLUMNS;
int total_h = a * LINES;
int x0 = 360 - total_w / 2;
int y0 = 240 - total_h / 2;
for (int i = 0; i < LINES; i++)
{
for (int j = 0; j < COLUMNS; j++)
{
// top-left corner of current cell
int x = x0 + j * a;
int y = y0 + i * a;
switch (maze[i][j])
{
case BOX:
draw_box(x, y, a, targets[i][j]);
break;
case SPACE:
draw_space(x, y, a, targets[i][j]);
break;
case WALL:
draw_wall(x, y, a);
break;
case PLAYER:
draw_player(x, y, a, targets[i][j]);
break;
}
}
}
for (i = 1; i < 5; i++)
draw_rect(x0-i, y0-i, total_w+2*i, total_h+2*i, COLOR_WHITE);
}
void victory()
{
beep();
draw_maze();
bmp_printf(FONT_LARGE, 300, 200, "YOU WIN :)");
}
void split_target()
{
for (int i = 0; i < LINES; i++)
{
for (int j = 0; j < COLUMNS; j++)
{
if (maze[i][j] == TARGET)
{
targets[i][j] = 1;
maze[i][j] = SPACE;
}
else if (maze[i][j] == BOX_ON_TARGET)
{
targets[i][j] = 1;
maze[i][j] = BOX;
}
else if (maze[i][j] == PLAYER_ON_TARGET)
{
targets[i][j] = 1;
maze[i][j] = PLAYER;
}
else targets[i][j] = 0;
}
}
}
void get_current_pos(int* l, int* c)
{
for (int i = 0; i < LINES; i++)
{
for (int j = 0; j < COLUMNS; j++)
{
if (maze[i][j] == PLAYER)
{
*l = i;
*c = j;
return;
}
}
}
// error
*l = *c = -1;
}
void move(int dl, int dc)
{
int l, c;
get_current_pos(&l, &c);
if (maze[l][c] != PLAYER) { printf("whoops"); return; }
switch (maze[l+dl][c+dc]) // what's in the destination box?
{
case SPACE: // simple, just move
{
maze[l][c] = SPACE;
maze[l+dl][c+dc] = PLAYER;
break;
}
case BOX: // move only if we can push the box in a free space
{
if (maze[l+dl*2][c+dc*2] == SPACE)
{
maze[l+dl*2][c+dc*2] = BOX;
maze[l+dl][c+dc] = PLAYER;
maze[l][c] = SPACE;
}
}
}
}
int check_solution()
{
for (int i = 0; i < LINES; i++)
{
for (int j = 0; j < COLUMNS; j++)
{
if ((targets[i][j]) && (maze[i][j] != BOX))
return 0; // at least one box not on target position
}
}
return 1; // looks alright
}
void main()
{
console_hide();
click(PLAY);
sleep(1);
fill_rect(0, 0, 720, 480, COLOR_EMPTY);
set_canon_gui(0);
split_target();
while(!check_solution())
{
draw_maze();
// print_maze();
int key = wait_key();
switch(key)
{
case LEFT:
case WHEEL_LEFT:
move(0, -1);
break;
case RIGHT:
case WHEEL_RIGHT:
move(0, 1);
break;
case UP:
case WHEEL_UP:
move(-1, 0);
break;
case DOWN:
case WHEEL_DOWN:
move(1, 0);
break;
case SET:
case UNPRESS:
break;
default:
printf("Exiting...\n");
set_canon_gui(1);
return;
}
}
printf("You win!\n");
victory();
sleep(5);
set_canon_gui(1);
}
main();