Introduction
Squares puzzle game is a very known and popular game that almost all of you have played using different platforms or tools. The very recent sample that I used was "Shuffle 'n Slide Brain Game" on Android. This game is all about slicing an image into 9 pieces that plotted randomly on a board so you are supposed to reorganize them to get the correct image.
Background
RingAllegro is a very powerful and simple extension of the Ring Programming Language that is based on Allegro library specialized in creating 2D games easily and fluently with high interaction capacity in a reasonable way of 2D game development ideas.
The Ring Programming Language is a recently released innovative and practical general-purpose multi-paradigm scripting language that can be embedded in C/C++ projects, extended using C/C++ code and/or used as standalone language. The supported programming paradigms are Imperative, Procedural, Object-Oriented, Functional, Meta programming, Declarative programming using nested structures, and Natural programming. The language is portable (Windows, Linux, Mac OS X, Android, etc.) and can be used to create Console, GUI, Web, Games and Mobile applications. The language is designed to be Simple, Small, Flexible and Fast. Its Dynamic Language (Dynamic Typing and Weakly Typed) that compiles the source code to byte code, then executes it by the Ring Virtual Machine, which is integrated with the Ring Compiler in one program.
Coding Principles in Ring
The coding process in Ring Programming Language has special principles, that we have to follow, which have been built to achieve high range of programming convenience between different programmers and developers, and to be easily used with its multi-paradigm philosophy. For now, I have to show some of them as necessary to disclose points that are important for reading to become smoother.
Ring Programming Language doesn't consider Main
method essential so that the written code will be executed from the first line to the end. In this case, all of code parts should be organized in a special manner following the next sequence:
- Files Loading
- Statements and global variables
- Functions
- Packages and classes
* Other things that we should consider about Ring coding are:
- Not case sensitive
- No explicit end for statements (No ; or ENTER is required)
- Call Function before definition
- No keyword to end Functions/Classes/Packages
Code Overview
The code is kind of self explanatory if you have some knowledge about RingAllegro and Allegro Library functions, so that I will try to walk through its main sections with some comments.
Initialization and Board Preparing
As a lot of programming language, Ring programming language starts its code with inclusions then variables definition.
Load "gamelib.ring"
BoardDim = 398 # Board dimension
SquarePostions = [[0,0],2,3,4,5,6,7,8,9] # Each position contain x,y
SquareDim = 0 # Square dimension
Moves = 0 # To count number of moves as to estimate the level of solving strategies
Squares = ["square",1] # Each square combined with its current position during playing
Then we have to initialize RingAllegro
library and create/prepare the main window that will contain playing board, sliced images and labels on and below the board.
# Initialization ===========
al_init()
al_init_image_addon()
al_init_font_addon()
al_init_ttf_addon()
font = al_load_ttf_font("pirulen.ttf",14,0 )
display = al_create_display(406,500)
al_clear_to_color(al_map_rgb(255,255,255))
Board = al_create_bitmap(boarddim,boarddim)
al_set_target_bitmap(board)
al_clear_to_color(al_map_rgb(220,220,220))
al_set_target_bitmap(al_get_backbuffer(display))
al_draw_bitmap(board,al_get_display_width(display) / 2 - boarddim /2,
al_get_display_height(display) / 2 - boarddim / 2,0)
Image = al_create_sub_bitmap(al_load_bitmap("palace.jpg"),0,0,boarddim - 8 ,boarddim - 8)
MoveLabel = al_create_bitmap(al_get_display_width(display),30)
al_set_target_bitmap(movelabel)
al_clear_to_color(al_map_rgb(220,220,220))
al_set_target_bitmap(al_get_backbuffer(display))
al_draw_text(font, al_map_rgb(0,0,0), 150, 20,ALLEGRO_ALIGN_LEFT, "Squares :)")
al_flip_display() # This function is very important after each change to be displayed
Then we have to initialize event listening mechanism of Allegro library which is composed of event queue and input registration like this:
# Events Initialization ===============================
event_queue = al_create_event_queue()
ev = al_new_allegro_event()
al_register_event_source(event_queue, al_get_display_event_source(display))
al_install_mouse()
al_register_event_source(event_queue, al_get_mouse_event_source())
al_install_keyboard()
al_register_event_source(event_queue, al_get_keyboard_event_source())
Then we have to determine the exact x,y of each position squares should assume, followed by slicing squares from all over the picture.
# SetSquaresPositionsAndDimensions =======================
squaredim = (boarddim - 8) / 3
boardx = al_get_display_width(display) /2 - boarddim /2
boardy = al_get_display_height(display) /2 - boarddim /2
SquarePostions[1] = [boardx +2 , boardy +2]
SquarePostions[2] = [boardx + squaredim + 4, boardy +2]
SquarePostions[3] = [boardx + squaredim * 2 + 6, boardy +2]
SquarePostions[4] = [boardx +2 , boardy + squaredim + 4]
SquarePostions[5] = [boardx + squaredim + 4, boardy + squaredim + 4]
SquarePostions[6] = [boardx + squaredim * 2 + 6, boardy + squaredim + 4]
SquarePostions[7] = [boardx +2 , boardy + squaredim * 2 + 6]
SquarePostions[8] = [boardx + squaredim + 4, boardy + squaredim * 2 + 6]
SquarePostions[9] = [boardx + squaredim * 2 + 6, boardy + squaredim * 2 + 6]
# CreateSquares ==========================
squares = list(9)
ind = 1
for j = 0 to 2
for i = 0 to 2
squares[ind] = [al_create_sub_bitmap
(image,i * squaredim, j * squaredim,squaredim,squaredim),-1]
ind += 1
next
next
squares[9] = [al_create_sub_bitmap(board,1,1,squaredim,squaredim),-1]
Playing and Interaction
Before starting the game challenge, we have to give the player a shot to make solving strategy easier.
# Draw full Pic with timer ===========
al_draw_bitmap(image,al_get_display_width(display) /2 - boarddim /2 + 4,
al_get_display_height(display) /2 - boarddim /2 + 4,0)
al_flip_display()
timer = al_create_timer(1)
al_register_event_source(event_queue, al_get_timer_event_source(timer))
al_start_timer(timer)
moves = 3
while true
al_wait_for_event(event_queue, ev)
al_draw_bitmap(movelabel,0,al_get_display_height(display) - 30,0)
al_draw_text(font, al_map_rgb(0,0,0), 200, al_get_display_height(display) - 20,
ALLEGRO_ALIGN_LEFT, string(moves))
moves -= 1
al_flip_display()
if moves = -1 moves = 0 exit ok
end
al_destroy_timer(timer)
al_rest(1)
al_draw_bitmap(movelabel,0,al_get_display_height(display) - 30,0)
al_draw_text(font, al_map_rgb(0,0,0), 150, al_get_display_height(display) - 20,
ALLEGRO_ALIGN_LEFT, "Moves : " + moves)
al_flip_display()
Now it's time to bring the player into the challenge by randomizing squares positions and draw them on the board.
# RandomizePositions ================================
for i = 1 to 9
while true
Ind = LimitRandom(1,9)
found = false
for s in squares
if ind = s[2] found = true ok
next
if found = false squares[i][2] = ind exit ok
end
next
# DrawRandomizedSquares ===============
al_set_target_bitmap(al_get_backbuffer(display))
al_draw_bitmap(board,al_get_display_width(display) / 2 - boarddim /2,
al_get_display_height(display) / 2 - boarddim / 2,0)
for s in squares
al_draw_bitmap(s[1],SquarePostions[s[2]][1],SquarePostions[s[2]][2],0)
next
al_flip_display()
Now it's time to put the ball in front of the player to make him/her show his/her best. :)
This could be done using while
loop which includes listening for the player interaction, applying his/her chosen move, then checking for the correct solution of the whole puzzle.
When the player completed the puzzle seccessfully, we have to estimate and give an opinion about his/her effort solving the puzzle depending on the number of moves he/she used.
while true
al_wait_for_event(event_queue, ev)
switch al_get_allegro_event_type(ev)
on ALLEGRO_EVENT_DISPLAY_CLOSE
exit
on ALLEGRO_EVENT_MOUSE_BUTTON_UP
mouse_x = al_get_allegro_event_mouse_x(ev)
mouse_y = al_get_allegro_event_mouse_y(ev)
for cursqr = 1 to 9
dx = mouse_x - SquarePostions[cursqr][1]
dy = mouse_y - SquarePostions[cursqr][2]
if dx < squaredim and dx > 0
if dy < squaredim and dy > 0
ns = CheckNearSpace(cursqr)
if ns != 0 exsquareposition(cursqr,ns)
ok
ok
ok
next
on ALLEGRO_EVENT_KEY_UP
switch al_get_allegro_event_keyboard_keycode(ev)
on ALLEGRO_KEY_UP
ps = GiveSelectedSqrInd(1)
if ps != 0 exsquareposition(ps,squares[9][2]) ok
on ALLEGRO_KEY_DOWN
ps = GiveSelectedSqrInd(3)
if ps != 0 exsquareposition(ps,squares[9][2]) ok
on ALLEGRO_KEY_LEFT
ps = GiveSelectedSqrInd(4)
if ps != 0 exsquareposition(ps,squares[9][2]) ok
on ALLEGRO_KEY_RIGHT
ps = GiveSelectedSqrInd(2)
if ps != 0 exsquareposition(ps,squares[9][2]) ok
off
off
if Solved() = true
ft = al_load_ttf_font("pirulen.ttf",20,0 )
lastpanel = al_create_bitmap(350,70)
al_set_target_bitmap(lastpanel)
al_clear_to_color(al_map_rgb(255,255,255))
al_set_target_bitmap(al_get_backbuffer(display))
al_draw_bitmap(lastpanel,al_get_display_width(display) /2 - boarddim /2 + 24,
al_get_display_height(display) /2 -30,0)
if moves < 50
al_draw_text(ft, al_map_rgb(0,0,0), 40, al_get_display_height(display) /2 - 20,
ALLEGRO_ALIGN_LEFT, "Congratulations ^_^")
al_draw_text(font, al_map_rgb(0,0,0), 80, al_get_display_height(display) /2,
ALLEGRO_ALIGN_LEFT, "You Are Really Genius")
but moves >= 50 and moves < 100
al_draw_text(ft, al_map_rgb(0,0,0), 40, al_get_display_height(display) /2 - 20,
ALLEGRO_ALIGN_LEFT, "Congratulations ^_^")
al_draw_text(font, al_map_rgb(0,0,0), 100, al_get_display_height(display) /2,
ALLEGRO_ALIGN_LEFT, "You Are Really Smart")
but moves >= 100 and moves < 200
al_draw_text(ft, al_map_rgb(0,0,0), 40, al_get_display_height(display) /2 - 20,
ALLEGRO_ALIGN_LEFT, "Congratulations ^_^")
al_draw_text(font, al_map_rgb(0,0,0), 100, al_get_display_height(display) /2,
ALLEGRO_ALIGN_LEFT, "You Are Smart")
but moves >= 200
al_draw_text(ft, al_map_rgb(0,0,0), 40, al_get_display_height(display) /2 - 20,
ALLEGRO_ALIGN_LEFT, "Congratulations ^_^")
al_draw_text(font, al_map_rgb(0,0,0), 90, al_get_display_height(display) /2,
ALLEGRO_ALIGN_LEFT, "But Slowly Solved")
ok
al_flip_display()
al_rest(15)
exit
ok
end
Finalization and Functions
After solving the puzzle, we have to finalize and destroy previously used RingAllegro
components like this:
# Finalization =================
al_destroy_event_queue(event_queue)
al_destroy_allegro_event(ev)
for s in squares
al_destroy_bitmap(s[1])
next
al_destroy_bitmap(image)
al_destroy_bitmap(board)
al_destroy_display(display)
al_exit()
Finally, we have finished the major code that executes this simple game but the rest of the lines of code that we didn't talk about are for the functions that have been defined and used locally in this game because this is the rule of code organization in Ring Language to declare classes and functions at the bottom of the code.
func LimitRandom S,E
while true
r = random(e)
if r >= s return r ok
end
func ExSquarePosition filled,spaced
al_set_target_bitmap(al_get_backbuffer(display))
al_draw_bitmap(squares[find(squares,filled,2)][1],
SquarePostions[squares[9][2]][1],SquarePostions[squares[9][2]][2],0)
al_draw_bitmap(squares[9][1],SquarePostions[filled][1],SquarePostions[filled][2],0)
squares[9][2] = filled
squares[find(squares,filled,2)][2] = spaced
moves += 1
al_draw_bitmap(movelabel,0,al_get_display_height(display) - 30,0)
al_draw_text(font, al_map_rgb(0,0,0), 150, al_get_display_height(display) - 20,
ALLEGRO_ALIGN_LEFT, "Moves : " + moves)
al_flip_display()
func CheckNearSpace i
S = squares[9][2]
switch i
on 1
switch s on 2 return s on 4 return s off
on 2
switch s on 1 return s on 3 return s on 5 return s off
on 3
switch s on 2 return s on 6 return s off
on 4
switch s on 1 return s on 5 return s on 7 return s off
on 5
switch s on 2 return s on 4 return s on 6 return s on 8 return s off
on 6
switch s on 3 return s on 5 return s on 9 return s off
on 7
switch s on 4 return s on 8 return s off
on 8
switch s on 5 return s on 7 return s on 9 return s off
on 9
switch s on 6 return s on 8 return s off
off
return 0
func GiveSelectedSqrInd Dir # Up 1, Right 2, Down 3, Left 4
S = squares[9][2]
switch s
on 1
switch dir on 1 return 4 on 4 return 2 off
on 2
switch dir on 1 return 5 on 2 return 1 on 4 return 3 off
on 3
switch dir on 1 return 6 on 2 return 2 off
on 4
switch dir on 1 return 7 on 4 return 5 on 3 return 1 off
on 5
switch dir on 1 return 8 on 2 return 4 on 3 return 2 on 4 return 6 off
on 6
switch dir on 1 return 9 on 2 return 5 on 3 return 3 off
on 7
switch dir on 3 return 4 on 4 return 8 off
on 8
switch dir on 2 return 7 on 3 return 5 on 4 return 9 off
on 9
switch dir on 2 return 8 on 3 return 6 off
off
return 0
func Solved
for i = 1 to 9
if squares[i][2] != i return false ok
next
return true
Points of Interest
The major point of interest that I have to declare here is the easiness and simplicity of coding journey using Ring Programming Language as I'm not supposed to worry about type of variables that I should use or type and size even dimension of arrays that can be used.
Other point is that this game could be played cross-platformly so that I could be comfortably porting it to other OS with a large ease.
The last thing I want to highlight is that, Allegro library has a very nice innovative way of programming simple games with already clear predefined functions so that I think I'm really lucky to find it already integrated with Ring Programming Language.
Resources
- Ring Programming Language Website
- Allegro Library Website
- This game's Github repository
History
- 30th April, 2016: Initial version