Tuesday, April 5, 2011

Wumpus planning 2

It's easy to represent a grid with a 2d array. You can even use a 1d array as long as you know when the next row is supposed to be (assuming an 8x8 grid array[63], elements 0-7 are row 1, 8-15 row 2, etc).

I've been trying to think of how to dynamically generate the grids. The first iteration is going to be hard coded most likely.

Mleh. Some of my most productive coding has been in the early morning (around 5-8am) oddly enough. Ana is on spring break so I haven't been awake at that time in almost two weeks. Coding in the evenings is almost impossible right now because I'm too tired from work or I've taken work home with me.

The first thing I'm going to do is come up with a level storage concept. Lets keep with the 8x8 level size. Each of the 64 elements needs to have the following defined:

1) Does it have pit or Wumpus clues in it?
2) Does it have a Wumpus, pit, or bat?
3) Does the player start here?

This could be solved with a 64 element array of structs.

struct room {
int Wumpus;
int Bat;
int Pit;
int Moss; // the pit clue
int Blood; // the Wumpus clue
int Player; //tells the future graphics device to render the player here
}

The level setup would init all of those to 0 (for false) and then make another pass that places the Wumpus, bat, and pit (and the clues, too) and then puts the player start somewhere far enough away from each (or not).

If I was feeling sassy I'd make it even more complicated by adding variables to define which rooms each room connects to - that way I could have rooms with tunnels. For example - lets say I'm in a room with exits to the North, South, East, and West. In the Wumpus game I played it was common to have one exit connect to another, like the North exit would loop around to the East such that going North or East would put you right back in the same spot.

Here's a screenshot of that: http://www.giantbomb.com/hunt-the-wumpus/61-13729/

But for now I'm not feeling sassy. I'm feeling depressed because I don't have time to code and I can't find a sensible 2d graphics option.

The horrible irony is that we never had problems with graphics back in the QBasic/Pascal days! We were rendering sprites defined by text files back then!

My kingdom for a 486 with a VGA card.

1 comment:

  1. This might be a good opportunity to introduce some basic software design patterns. Let's start with the ubiquitous "Model-View-Controller" architecture:

    http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller

    The gist of an MVC approach to this example is that you want to keep your game state data (the Model) separate from the user interface (the View). You then regulate the interaction of the two with code that describes the game's dynamics, or Controller code.

    You've already discovered one very good motivation for doing this: you're having trouble deciding on a GFX package but you'd still like to make progress on the game. If you keep your View decoupled from your Model you can develop Model code independently of your UI. This lets you design the game and test it with a text interface, and then reuse all your code when you jump to GFX.

    So, back to your example.

    Model
    -------
    Your model will be the (structured) data needed to store the state of your game and describe the relationships between each game object.

    Choosing your objects and associated data structures is a fairly open ended problem and depends heavily on your design goals, but here's how I would do it:

    - Room: a data structure that contains a '(char)room_type' and pointers to adjacent rooms '(room*)(north,south,east,west)'. 'room_type' can be a simple (8-bit) code that describes the room's contents (wumpus,bat,clue,etc.).

    - Game: a data structure that contains the global game state attributes. I would probably store a game result '(char)game_result', a pointer to the current room occupied by the player '(room*)current_room', Wumpus room '(room*)wumpus_room' and an array of pointers to all the rooms in the game '(room*)rooms[MAX_NUM_ROOMS]'.

    Controller
    ------------
    This code will handle the initialization of the model and the transitions between model states, which are mainly executed in response to user input. These are the 'actions' that you will need to implement to make your game go.

    - Init((Game*)game_obj):

    Generates the game initial values. This should probably include room generation, room connection, and initial wumpus/bat/player placement. The room connection algorithm will likely be the tricky part here.

    If you use my pointer-based data structure, you'll want to malloc() your rooms. Do that here too.

    Obviously, this function should be executed at each game reset.

    - Exit((Game*)game_obj):

    Call this function to cleanly tear down the Model data (e.g. call free() on your rooms) when the game is over.

    - Move((Game*)game_obj,(char)direction):

    Handles all the decision making associated with player movement. This includes using the adjacent room pointers (e.g. 'game_obj->current_room->north') to update the value of 'current_room'.

    - Shoot((Game*)game_obj,(char)direction):

    Handles all the decision making associated with shooting the Wumpus. This is only a complicated function if you want to have the Wumpus move to a new room on a missed shot. If not, this function will set the final game result, e.g. (game_obj->game_result=WUMPUS_ATE_YOU).

    View
    ------
    I'll leave this part up to you, but the View code should include functions for displaying the game board, processing user input, and reporting the game result to the user.

    It should always treat the data contained in the Model as 'read-only' and instead use the functions defined in the Controller to change the state of the Model.

    ReplyDelete