Report About Sokoban Game Design Project

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 21

Sokoban Game

Course Design Purpose


The process and practice of developing high-quality learning settings and experiences for students
is known as course design. Students can access knowledge, acquire skills, and practise higher
levels of thinking through purposeful and planned exposure to instructional materials, learning
activities, and interaction. The goal of course design is to create the best learning experiences for
students in an atmosphere that encourages and values learning and intellectual development.
The context for good course design is that the courses themselves serve as the basis for teaching
and learning. More students will be able to participate in richer learning experiences that support
successful learning if the design is effective. The science of good course design is respected at
university, and all course components are purposeful. Courses, whether in general education or
program-specific education, must serve as the basis for student learning. As a consequence, good
course design should result in our programs having a positive impact and producing the desired
student results.

Course Design Idea


Sokoban is a puzzle video game in which the player pushes boxes around in a warehouse, trying
to get them to storage locations. The game was designed in 1981 by Hiroyuki Imabayashi, and
first published in December 1982.
the game is played on a board of squares, where each square is a floor or a wall. Some floor
squares contain boxes, and some floor squares are marked as storage locations.
The player is confined to the board and may move horizontally or vertically onto empty squares
(never through walls or boxes). The player can move a box by walking up to it and pushing it to
the square beyond. Boxes cannot be pulled, and they cannot be pushed to squares with walls or
other boxes. The number of boxes equals the number of storage locations. The puzzle is solved
when all boxes are placed at storage locations.

Implementation
Game.h
#include <stdio.h>

// default console screen size


#define SCREEN_WIDTH 80
#define SCREEN_HEIGHT 25
#define SCREEN_SIZE SCREEN_WIDTH * SCREEN_HEIGHT

// screen buffer that replaces stdout


char screen[SCREEN_SIZE];

// init windows headers


#ifdef WIN32

#include <windows.h>
#include <stdlib.h>
HANDLE console;
CONSOLE_CURSOR_INFO cursor;
COORD coord;
DWORD chars_to_write = 0;

#endif

// init unix headers


#ifdef unix

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>

#endif
void InitScreen()
{
// print directly to screen buffer
#ifdef WIN32
console = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL,
CONSOLE_TEXTMODE_BUFFER, NULL);
cursor.dwSize = 100;
cursor.bVisible = FALSE;
coord.X = 0;
coord.Y = 0;
SetConsoleActiveScreenBuffer(console);
SetConsoleCursorInfo(console, &cursor);
#endif

// use ANSI VT100 escape sequences hide cursor and clear screen
#ifdef unix
printf("\x1b[?25l");
printf("\x1b[2J");
#endif
}

void RefreshScreen()
{
// might not be needed on windows natively, needed on wine though
for(int scr_cell = 0; scr_cell < SCREEN_SIZE; scr_cell++)
if(!screen[scr_cell]) screen[scr_cell] = ' ';

// update screen buffer


#ifdef WIN32
screen[SCREEN_SIZE - 1] = 0;
WriteConsoleOutputCharacter(console, screen, SCREEN_WIDTH *
SCREEN_HEIGHT, coord, &chars_to_write);
#endif

// print screen buffer to stdout at coordinates 0, 0


#ifdef unix
printf("\x1b[0;0H%s", screen);
#endif
}

void PrintMap(int pos_x, int pos_y, int map_width, int map_height, char *map)
{
for (int row = 0; row < map_height; row++)
{
for (int col = 0; col < map_width; col++)
{
screen[(row + pos_y) * SCREEN_WIDTH + col + pos_x] = map[row * map_width +
col];
}
}

RefreshScreen();
}

// getchar() for windows without echoing


#ifdef WIN32
int getch()
{
DWORD mode, chars_to_read;
HANDLE console = GetStdHandle(STD_INPUT_HANDLE);

GetConsoleMode(console, &mode);
SetConsoleMode(console, mode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT));
int key = 0;
ReadConsole(console, &key, 1, &chars_to_read, NULL);
SetConsoleMode(console, mode);

return key;
}

#endif

// getchar() for unix without echoing


#ifdef unix

int getch()
{
struct termios oldattr, newattr;
tcgetattr( STDIN_FILENO, &oldattr );
newattr = oldattr;
newattr.c_lflag &= ~( ICANON | ECHO );
tcsetattr( STDIN_FILENO, TCSANOW, &newattr );
int key = getchar();
tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );
return key;
}

#endif

void Leave()
{
#ifdef WIN32
cursor.bVisible = TRUE;
SetConsoleCursorInfo(console, &cursor);
#endif

// show cursor escape sequence


#ifdef unix
printf("\x1b[?25h");
#endif
}

Sokoban-game.c
#include "game.h"

#define MAP_WIDTH 8
#define MAP_HEIGHT 10
#define PLAYER_POSITION pos_y * MAP_WIDTH + pos_x
char map[] = {

"##### "
"#xB ### "
"### # "
"#x@B # "
"### Bx# "
"#x##B # "
"# # x ##"
"#B OBBx#"
"# x #"
"########"

};

int dest_squares[10]; // array to store cell indexes for 'x' cells

int GetDestSquares() // init 'x' cells indexes


{
int count = 0, cell; // 'x' cell number, current cell index

for(int row = 0; row < MAP_HEIGHT; row++) // loop ower map rows
{
for(int col = 0; col < MAP_WIDTH; col++) // loop ower map columns
{
cell = row * MAP_WIDTH + col; // init current cell index

if(map[cell] == 'x' || map[cell] == 'O') // if 'x' cell is emty or with box on it


dest_squares[count++] = cell; // store it in array
}
}

return count; // return number of 'x' cells


}

void GetPosition(int *pos_x, int *pos_y)


{
int cell; // current cell index

for(int row = 0; row < MAP_HEIGHT; row++) // loop ower map rows
{
for(int col = 0; col < MAP_WIDTH; col++) // loop ower map columns
{
cell = row * MAP_WIDTH + col; // init current cell index

if(map[cell] == '@') // if current cell on the map contains player


{
*pos_x = col; // store player's x coordinate
*pos_y = row; // store player's y coordinate
}
}
}
}

void MoveCharacter(int pos_x, int pos_y, int offset)


{
if(map[PLAYER_POSITION + offset] != '#') // if player doesn't hit the wall
{
if(((map[PLAYER_POSITION + offset] == 'B') || // if player hits the box
(map[PLAYER_POSITION + offset] == 'O')) && // or the box on 'x' cell
(map[PLAYER_POSITION + offset * 2] != '#' || // and box doesn't hit a wall
map[PLAYER_POSITION + offset * 2] != 'B' || // or another box
map[PLAYER_POSITION + offset * 2] != 'O')) // or box on 'x' cell
{
map[PLAYER_POSITION] = ' '; // clear previous player's position
pos_x += offset; // update player's coordinate

if(map[PLAYER_POSITION + offset] == ' ') // if the square next to the box


is empty
map[PLAYER_POSITION + offset] = 'B'; // push the box

else if(map[PLAYER_POSITION + offset] == 'x') // if the square next to the


box is 'x'
map[PLAYER_POSITION + offset] = 'O'; // mark the box is on it's place

else
{
map[PLAYER_POSITION - offset] = '@'; // if box hits the wall or
another box
return; // don't push it any further
}

map[PLAYER_POSITION] = '@'; // draw the player in the new


position
}
else // if the square next to the player is empty
{
map[PLAYER_POSITION] = ' '; // clear previous player position
pos_x += offset; // update player's coordinate
map[PLAYER_POSITION] = '@'; // draw the player in the new
position
}
}
}
Functions of Main Modules in the System
int main()
{
InitScreen();

int key; // user input key


int pos_x, pos_y; // player's coordinates
int dest_count; // 'x' cells counter

int dest_num = GetDestSquares();

int center_x = SCREEN_WIDTH / 2 - MAP_WIDTH / 2;


int center_y = SCREEN_HEIGHT / 2 - MAP_HEIGHT / 2;

PrintMap(center_x, center_y, MAP_WIDTH, MAP_HEIGHT, map);

while(1)
{
if(key == 27) break;

key = getch();
GetPosition(&pos_x, &pos_y);

switch(key)
{
case 'w': MoveCharacter(pos_x, pos_y, - MAP_WIDTH); break;
case 's': MoveCharacter(pos_x, pos_y, MAP_WIDTH); break;
case 'a': MoveCharacter(pos_x, pos_y, - 1); break;
case 'd': MoveCharacter(pos_x, pos_y, 1); break;
}

dest_count = 0; // reset 'x' cells counter

for(int i = 0; i < 10; i++) // for all destination squares


{
if(map[dest_squares[i]] == 'O') dest_count++; // increase 'x' cells counter if box
is on 'x' cell

if(map[dest_squares[i]] == ' ') // if 'x' cell has been erased


map[dest_squares[i]] = 'x'; // restore it
}

PrintMap(center_x, center_y, MAP_WIDTH, MAP_HEIGHT, map);

// if all boxes are on it's places break out of game loop


if(dest_num == dest_count)
{
sprintf(screen + (SCREEN_WIDTH * SCREEN_HEIGHT / 2) - MAP_WIDTH / 2,
"YOU WIN!");
RefreshScreen();
break;
}
}

Leave();

return 0;
}

Sokoban.c
#include <stdio.h>
#include <string.h>

#define MAP_WIDTH 8
#define MAP_HEIGHT 9
#define PLAYER_POSITION pos_y * MAP_WIDTH + pos_x

char map[] = {

" ##### \n"


"### # \n"
"#x@B # \n"
"### Bx# \n"
"#x##B # \n"
"# # x ##\n"
"#B OBBx#\n"
"# x #\n"
"########\n"

};

/*#define MAP_WIDTH 14
#define MAP_HEIGHT 10
#define PLAYER_POSITION pos_y * MAP_WIDTH + pos_x

char map[] = {

"##############\n"
"# # xB #\n"
"# x # xB #\n"
"# B # xB #\n"
"# #### #\n"
"# @ # #\n"
"# # #\n"
"# B # #\n"
"# x #\n"
"##############\n"

};*/

int dest_squares[10]; // array to store cell indexes for 'x' cells

int GetDestSquares() // init 'x' cells indexes


{
int count, cell; // 'x' cell number, current cell index

for(int row = 0; row < MAP_HEIGHT; row++) // loop ower map rows
{
for(int col = 0; col < MAP_WIDTH; col++) // loop ower map columns
{
cell = row * MAP_WIDTH + col; // init current cell index

if(map[cell] == 'x' || map[cell] == 'O') // if 'x' cell is emty or with box on it


dest_squares[count++] = cell; // store it in array
}
}

return count - 1; // return number of 'x' cells


}

void GetPosition(int *pos_x, int *pos_y)


{
int cell; // current cell index

for(int row = 0; row < MAP_HEIGHT; row++) // loop ower map rows
{
for(int col = 0; col < MAP_WIDTH; col++) // loop ower map columns
{
cell = row * MAP_WIDTH + col; // init current cell index

if(map[cell] == '@') // if current cell on the map contains player


{
*pos_x = col; // store player's x coordinate
*pos_y = row; // store player's y coordinate
}
}
}
}
void MoveCharacter(int pos_x, int pos_y, int offset)
{
if(map[PLAYER_POSITION + offset] != '#') // if player doesn't hit the wall
{
if(((map[PLAYER_POSITION + offset] == 'B') || // if player hits the box
(map[PLAYER_POSITION + offset] == 'O')) && // or the box on 'x' cell
(map[PLAYER_POSITION + offset * 2] != '#' || // and box doesn't hit a wall
map[PLAYER_POSITION + offset * 2] != 'B' || // or another box
map[PLAYER_POSITION + offset * 2] != 'O')) // or box on 'x' cell
{
map[PLAYER_POSITION] = ' '; // clear previous player's position
pos_x += offset; // update player's coordinate

if(map[PLAYER_POSITION + offset] == ' ') // if the square next to the box


is empty
map[PLAYER_POSITION + offset] = 'B'; // push the box

else if(map[PLAYER_POSITION + offset] == 'x') // if the square next to the


box is 'x'
map[PLAYER_POSITION + offset] = 'O'; // mark the box is on it's place

else
{
map[PLAYER_POSITION - offset] = '@'; // if box hits the wall or
another box
return; // don't push it any further
}
map[PLAYER_POSITION] = '@'; // draw the player in the new
position
}

else // if the square next to the player is empty


{
map[PLAYER_POSITION] = ' '; // clear previous player position
pos_x += offset; // update player's coordinate
map[PLAYER_POSITION] = '@'; // draw the player in the new
position
}
}
}

int main()
{
int key; // user input key
int pos_x, pos_y; // player's coordinates
int dest_count; // 'x' cells counter

int dest_num = GetDestSquares(); // get number of 'x' cells

printf("%s\n", map); // print map

while(key != 27) // game loop


{
GetPosition(&pos_x, &pos_y); // get player's coordinates

key = getchar(); // get user input


switch(key)
{
// move character up
case 'w':
MoveCharacter(pos_x, pos_y, - MAP_WIDTH - 1);
break;

// move character down


case 's':
MoveCharacter(pos_x, pos_y, MAP_WIDTH + 1);
break;

// move character left


case 'a':
MoveCharacter(pos_x, pos_y, -1);
break;

// move character right


case 'd':
MoveCharacter(pos_x, pos_y, 1);
break;

dest_count = 0; // reset 'x' cells counter

for(int i = 0; i < 10; i++) // for all destination squares


{
if(map[dest_squares[i]] == 'O') dest_count++; // increase 'x' cells counter if box
is on 'x' cell

if(map[dest_squares[i]] == ' ') // if 'x' cell has been erased


map[dest_squares[i]] = 'x'; // restore it
}

printf("%s\n", map); // print map

// if all boxes are on it's places break out of game loop


if(dest_num == dest_count)
{
printf("You win!\n");
key = 27;
}
}

return 0;
}
Relationship between Modules
The game is linked between two files, the first of which is the game file, which is a
file that I use to link it to the other file called sokoban - game, which I mainly use in
the main file of the game that contains the main

Course Design Experience


Learning experience design is a synthesis of numerous design disciplines and the subject of
learning. Interaction design, user experience design, experience design, visual design, and game
design are some of the key design elements employed in LXD. These design ideas are coupled
with educational, training, and development, cognitive psychology, experiential learning,
educational sciences, and neuroscience features.
It is a reality that all we learn originates from. As previously said, an experience is any
circumstance that requires time and makes an imprint. These events do not have to take place in
an instructional context such as a school. They can happen at home, outside, at work, or
anyplace else.
Not every experience is equally educational. Some encounters are simply uninteresting or
irritating. Fortunately, we've all had highly instructional experiences that will last a lifetime.
The ability to create such compelling experiences is the primary characteristic of a competent
LX designer.
Example

You might also like