Making a 3x3 slider puzzle, You know the kind.

Started by Glenjamin, Wed 29/07/2015 00:43:23

Previous topic - Next topic

Glenjamin


I'm currently working on a slider puzzle for my adventure game. Each puzzle piece is 28x26. The background for the puzzle is 83x76. There's going to be an empty space in it that will be filled in once the puzzle is completed. I just don't know what to do or where to start. Thanks!

Slasher

i take it that the tile pieces are 28 x 26 each and each piece covers 83 x 76 as border (space).

if this is all just part of the background you could change the background frame to show tiles with a completed background frame.

Code: ags

//Game starts (room load before fade in) as shows game start background frame
SetBackgroundFrame(0); // normal main background

//after completion change background frame to completed frame
SetBackgroundFrame(1); // imported frame on completion


that's if i understand you correctly..





Monsieur OUXX

Hello,

Warning: I'm not sure that slasher's answer is related to your issue at all.

Here is my suggestion :

Note: If you're confident with programming, you can base your puzzle on rendering each tile individually onto one large Drawingsurface, using the graphical built-in functions. Imho, that would be reinventing the wheel.
I think this would be much easier:
1) Create as many GUIButtons as there will be tiles. The advantage of GUIButtons is that: a) you can set an image into them. b) you can intercepot the click onto them. Please note that this could be a pain if you had many tiles, but since you have only 9, this will be easily manageable.
2) For convenience, create a module where you will put all the logical code for your minigame (if you need some help, ask). In the header, create a struct that represents the minigame, containing as many static functions as there will be possible actions in your game (for example: import static void on_click_tile(int whichTile); that will be called when the player clicks on one of the tiles.
Example;
Code: ags

struct MyMiniGame
{
   import static void on_click_tile(int whichTile);
};

If you have no idea about modules, then put all the code on the main script, I personally hate doing that.
3) Inside your module's body, create another struct that you will use to represent the individual tiles. for example something like this:
Code: ags

struct TilesStruct
{
     GUIButton* button;
     int positionX; //the position in the GRID (x=0, y=0 means top-left. x=2,y=2 means bottom-right)
     int positionY;
}
TilesStruct tilesArray[9];

You can now access the data of any tiles. for example, tilesArray[0].button.X will tell you the X coordinate (in screen pixels, inside the Gui) of the very first tile (tile #0)
4) Still inside your module, add standard function game_start() . This function is automatically called by AGS when the game starts.
5) Inside that function, bind all the GUIButton of the Gui to your data structure:
Code: ags

tilesArray[0].button = MyButton0;
tilesArray[0].button = MyButton1;
...
tilesArray[8].button = MyButton8;

the advantage of this is that from now on,  you can now manipulate the buttons only by using their number (0 to 8) and doing abstract calculations on that, without needing to write hard-written code for each button (MyButton0, MyButton1...) which is always a pain in the back.
6) In the Editor, create the "click" event/function for each button, and inside, call the function that you created earlier to react to a button click.
Example:
Code: ags

//this function created automatically by AGS when you set a "any click on button" event in the Editor.
//there will be 9 functions like this
function MyButton0_OnClick()
{
    MyMiniGame.on_click_tile(0);
}


Now you have the skeletton. You still need to program the logic of the game (which tile can move in which direction, etc.)
I just gave a general explanations without detailing each step.
If you have any questions, don't hesitate. Ask!
 

Snarky

Mr. OUXX posted while I was writing. I'm sure his solution is good - in fact, probably better than this - but here's an alternative approach. Since they are quite different, don't try to combine this solution with his, but choose one or the other.

You want to know how to script the whole puzzle? It's going to take a good deal of code, I'm afraid.

I'd make the puzzle board a room, and add the pieces as objects. I'd have an array that kept track of the logical state of the puzzle, and update the positions of the pieces based on that. I'd use the Tween module to slide the pieces and to fade in the final piece when the puzzle is solved.

Here's a quick outline: Set up the room and the pieces (as objects). Make the first piece (in the solved puzzle) object 0, and so on, in reading order.

We don't want to do all the logic just based on screen coordinates: it's much easier if we have a model of the puzzle rows and columns. But because AGS doesn't have 2D arrays, we'll have to do a plain array and do the mapping to rows and cols ourselves. Then I'd have a couple of variables to keep track of the empty slot.

Code: ags

#define PUZZLE_COLS 3
#define PUZZLE_SIZE 9

// Puzzle state
Object* puzzle[PUZZLE_SIZE];

// Empty slot
int emptyRow;
int emptyCol;


Then you need to fill the puzzle with the pieces in jumbled order and set the empty slot position. I'll skip that part.

You also need to display the puzzle, so that what players see is synchronized with the logical state of the puzzle:

Code: ags

#define PUZZLE_PIECE_WIDTH 28
#define PUZZLE_PIECE_HEIGHT 26

int puzzleX = 50; // Set this to the top left X-coordinate of the puzzle
int puzzleY = 70; // Set this to the top left Y-coordinate of the puzzle

void displayPuzzle()
{
  int row=0;
  int i=0;
  while(row < PUZZLE_SIZE/PUZZLE_COLS)
  {
    int col=0;
    while(col < PUZZLE_COLS)
    {
      if(puzzle[i] != null)
      {
        puzzle[i].X = puzzleX + col*PUZZLE_PIECE_WIDTH;
        puzzle[i].Y = puzzleY + row*PUZZLE_PIECE_HEIGHT;
      }
      i++;
      col++;
    }
    row++;
  }
}


Now if someone clicks on a puzzle piece, you have to check if it's adjacent to the empty slot, and if so, slide it there. I'll skip that for now, and just do the bit to check if the puzzle is solved:

Code: ags

bool isPuzzleSolved()
{
  // Check if puzzle is solved, and return true or false
  int i=0;
  while(i<PUZZLE_SIZE - 1)  // This assumes that in the solved puzzle, the empty space is the last one
  {
    if(puzzle[i] != object[i])
      return false;
    i++;
  }
  return true;
}

Monsieur OUXX

The tween module is a nice icing on the cake to make the pieces move smoothly.
Also, as you saw, snarky uses room Objects instead of a gui with Buttons.

What we both do is using an array to manipulate the tiles based on their index, hence avoiding the hassle of giant "if" blocks and having to name each tile by its individual name. That's the central part, that beginners usually have trouble understanding.
 

Glenjamin

I'm gonna be perfectly honest here, this is WAY over my head. I never thought that a puzzle that appears in almost every adventure game could be this complex. I've decided to go with Snarky's method, although I'm not sure where to put the snippets of code or what variables I should be creating. If you could clarify, that would be amazing. You don't have to but it would be greatly appreciated. Thanks again.

Snarky

(The explicit answer to your question is at the end, but if you feel in over your head, this explanation might help.)

It's not actually particularly difficult, it's just that there's no built-in support for it, so you have to write it all yourself, and computer programming is fiddly. It's a little surprising that no one has made a module for it, but slider puzzles have a pretty bad reputation among many adventure fans, so that could be why.

If you're happy having the puzzle as a room, I would probably just put all the code in the room script.

To approach a task like this, it's often useful to break it down into a few questions or steps:

Q0. How do you actually want it to work, in detail? Particular requirements might affect the best way to write it.

-Is the empty slot (in the solved puzzle) in a particular position, e.g. the last slot?
-Is this definitely just one puzzle, or is it possible that you'll have several different slider puzzles in the game?
-Is the puzzle fixed in a particular location in the game, or is it something the player carries around with them and can attempt anywhere?
-Do you want animations (pieces sliding), or is it OK for them to just jump from one slot to another?
-Do you want to move pieces by clicking on them (easy), or actually have players use drag-and-drop gestures to slide them themselves (harder)?
-Do you want to be able to slide multiple pieces together? (Hardly necessary in a 3x3 puzzle, I'd think.)
-If players leave and come back, should the puzzle remain as they left it, or reset?
-Does the puzzle always start in the same state, or should it be randomized each time?

(Most of these don't make a huge difference to this particular approach, but some answers might require a different solution.)

Q1. How can we get AGS to display what we want and respond to relevant user actions? (In this case, display tiles in different arrangements, and move them when clicked.)

A. Several possibilities, including making the tiles room objects, or buttons on a GUI. In each case, the interaction is most easily handled by using the OnClick event. (This implies that the code starts out knowing which tile was clicked, but not automatically which slot it is in.)

Q2. What do we need to keep track of to make the logic of the puzzle work? What datastructures do we need?

A. Basically, we just need to know the arrangement of the tiles. A plain array of tile indexes (whether as ints or pointers to the objects or buttons) will do fine. Also, while not strictly necessary, it simplifies things a bit to explicitly keep track of the empty slot.

Q3. What are the tasks we need to be able to perform in the puzzle? What are the things we need to be able to deal with?

A. Here are some useful features we need to make a slider puzzle:
-Set up the puzzle in some arrangement; usually this is handled by a function called init()
-You might want to be able to randomize the initial state, but we can leave that for later; to do so, you might have a function called randomize()
-Display the puzzle on screen (arrange the buttons or objects to match the model of the tiles in our array); this is the displayPuzzle() function above
-Check if a particular tile can move
-Move a certain tile into the empty spot
-Check if the puzzle is solved; this is the isPuzzleSolved() function above
-Display the final piece

Each of these functions is essentially some test or update of our variables (the array of tile indexes and the variables to keep track of the empty slot), or our objects/buttons, as the examples in the previous post demonstrate. It's a bit tedious to write, but shouldn't be too difficult, as each task is pretty simple, individually.

Q4. So how do we link those functions together and trigger them to happen at the right time?

A. You can see that if we have all these functions, it's not that difficult to wire up the whole puzzle. We just need to run the right ones when particular events happen:

-User enters room (puzzle starts): init()/randomize(), then displayPuzzle()
-User clicks on a tile: check if tile can move, if yes, move tile and update the display, then check if puzzle is solved, if so, display final tile

Tada!

Glenjamin


ollj

A good starting point for any computational/mathematical problem, like basic famous games/simulations, maze generators, basic pathfinding, sorting algorythms up to any calculus/algebra problem are:
- youtube tutorials (all the below often have a video version somewhere, even if its just a runtiome example)
- searching for opensource examples, by searching for "... in c" "... in java" "... in actionscript" "... in html5"
- lectures, manuals and publications, by searching for " pdf"

SMF spam blocked by CleanTalk