Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / game

Squares Puzzle using RingAllegro

5.00/5 (3 votes)
1 May 2016CPOL4 min read 20.1K   63  
2D games programming simplicity using RingAllegro Library

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:

  1. Files Loading
  2. Statements and global variables
  3. Functions
  4. Packages and classes

* Other things that we should consider about Ring coding are:

  1. Not case sensitive
  2. No explicit end for statements (No ; or ENTER is required)
  3. Call Function before definition
  4. 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.

Ring
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.

Ring
# 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:

Ring
# 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.

Ring
# 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.

Ring
# 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.

Ring
# 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.

Ring
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:

Ring
# 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.

Ring
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

  1. Ring Programming Language Website
  2. Allegro Library Website
  3. This game's Github repository

History

  • 30th April, 2016: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)