= The game I programmed for Open Jam 2020 :Author: Klaatu :Email: <klaatu@member.fsf.org> :Revision: 1.0 This year, I joined in Open Jam, a "game jam" in which programmers from around the world dedicate a weekend to create open source games. The jam is essentially an excuse to spend a weekend coding and the majority of the games that come out of the challenge are small distractions rather than something you're likely to play for hours upon end. But they're fun, diverse, and open source, and that's a pretty good feature list for a game. The game I submitted is https://notklaatu.itch.io/unveil[Unveil], a calming puzzle game in which the player must first discover the goal, and then work to achieve the goal with the greatest number of points. Because part of the game is the discovery process, I won't reveal any more about the game play than that. image:unveil-2.jpg The whole game is only 338 lines, written in Python using the Pygame module. It's of course open source, and part of it may serve as a good introduction to a few programming concepts that used to confound me (two-dimensonal arrays being the most significant). For simple game design, a two-dimensional array is very useful, because so many enduring games are built upon them. You can probably think of several, although if you don't know what a two-dimensional array is, you may not realise it. == Arrays in gaming An array is a collection of data. An array can be listed across a page, or an X-axis (in mathematical terms). For instance: ----[source,text] artichoke, lettuce, carrot, aubergine, potato ---- An array may also be represented as a list, or a Y-axis: ----[source,text] artichoke lettuce carrot aubergine potato ---- This is a one-dimensional array. A two-dimensional array extends on both the X-axis and Y-axis. Here's a common two-dimensional array seen in the world of board games: image:chess.jpg[GNU chess] Yes, two-dimensional arrays are used as the board for chess, draughts, noughts and crosses (tic-tac-toe), https://opensource.com/article/18/5/maptool[RPG battles maps], https://opensource.com/article/19/9/advanced-bash-building-minesweeper[minesweeper], Carcassonne, Forbidden Island, and in slightly modified forms, games like Monopoly and even https://otagomuseum.nz/athome/the-royal-game-of-ur[Ur] (literally the oldest game we know of). If you can comfortably create a two-dimensional array, you have a great start at programming any number of games. == Creating tiles in Pygame If you're not familiar with Python, you should take some time to review this https://opensource.com/article/17/10/python-101[Python (and Pygame) introductory series]. For the purposes of this article, and I'll call the individual squares in the game board array as _tiles_. To construct a two-dimensional array, or game board as the case may be, you must have tiles. In object-oriented programming, you consider each component as a unique object based upon a template (or _class_, in programming terminology). So before creating the game board, you must first create the infrastructure for creating the building blocks of the board: tiles. First, set up a simple Pygame project, creating a display (your window into the game world), a group to represent the game board, and a few standard variables. ----[source,python] import pygame pygame.init() game_world = pygame.display.set_mode((960, 720)) game_board = pygame.sprite.Group() running = True black = (0, 0, 0) white = (255, 255, 255) red = (245, 22, 22) world_x = 960 world_y = 720 ---- Next, create a Tile class to establish the template from which each tile gets cast from. The first function initializes a new tile when one is created, giving it the necessary basic fields: width, height, an image (actually I just fill it with the color white), and whether or not it's active. In this case, I use `is_pressed`, as if the tile is a button, because that's what it'll look like is happening when the code is finished: when the user clicks a tile, it changes color as if it were a button being lit up. For other purposes, this state needn't be visible. In chess, for example, you might instead have a field to represent whether a tile is occupied and, if so, by what kind of chess piece. ----[source,python] class Tile(pygame.sprite.Sprite): def __init__(self, x, y, w, h, c): pygame.sprite.Sprite.__init__(self) self.image = pygame.Surface((w, h)) self.image.fill(c) self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y self.is_pressed = False ---- The second function is an update function. Specifically, it checks whether a tile has been clicked by the user. This requires mouse coordinates, which you'll get later in the code during the event loop. For this demonstration, I make this function fill the tile with the color red when it's in the `is_pressed` state, and back to white otherwise. ----[source,python] def was_clicked(self, mouse): if self.rect.collidepoint(mouse) and not self.is_pressed: self.image.fill(red) self.is_pressed = True elif self.rect.collidepoint(mouse) and self.is_pressed: self.image.fill(white) self.is_pressed = False else: return False ---- == Main loop The main loop of this demo is simple. It checks for two kinds of input: a quit signal and a mouse down (click) event. When a mouse click is detected, the `was_clicked` function is called to react (filling it with red or white, depending on its current state). Finally, the screen is filled with black, and the game board state is updated and the screen redrawn.. ----[source,python] """ holding place for game board construction """ while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.MOUSEBUTTONDOWN: for hitbox in game_board: hitbox.was_clicked(event.pos) game_world.fill(black) game_board.update() game_board.draw(game_world) pygame.display.update() pygame.quit() ---- == Board construction To build a two-dimensional array, you must first decide how many tiles you want in both directions. I'll use 8 for this example because that's how a chessboard is constructed, but you could use fewer or more. You could even accept arguments at launch to define the array's size depending on options, such as `--row` and `--column`. ----[source,python] rows = 8 columns = 8 ---- Because you don't know the size of the board, you must calculate the width of the rows and columns based on the size of your display. I also include 1 pixel of padding between each tile, because without a gap it looks like one big block of colour. ----[source,python] column_width = world_x / columns row_height = world_y / rows pad = 1 ---- Laying out tiles just across the display is simple. Of course, this isn't the goal, as it only draws along the X-axis, but it's a good start: ----[source,python] j = 0 while j < rows: tile = Tile(j * column_width, row_height, column_width - pad, row_height - pad, white) game_board.add(tile) j += 1 ---- The idea here is that the variable `j` starts at 0, and so the first tile is placed from 0 to `column_width`, less the value of the padding. Then the variable `j` is incremented to 1, and so the next tile is placed at 1 times the value of `column_width`, and so on. You can run that code to see the partial success it renders. What this solution obviously lacks is any awareness of further rows. Use a second counter variable to track rows. ----[source,python] j = 0 k = 0 while j < rows: while k < columns: tile = Tile(k * column_width, j * row_height, column_width - pad, row_height - pad, white) game_board.add(tile) k += 1 j += 1 k = j - (1*j) ---- In this code block, which achieves the desired result, each tile is positioned in a space determined by the current value of either `j` or `k`. The `k` variable is incremented within its loop so that each tile is progressively placed along the X-axis. The `j` variable is incremented outside the nested loop so that everything gets moved down one row. The `k` variable is then set to 0 so that when the inner loop starts again, everything is shunted back to the far left of the screen. image:2darray.jpg[Game board] == Easy arrays Creating a grid can seem mathematically and syntactically intensive, but with this example plus a little bit of thought about what result you want, you can generate them at will. The only thing left for you to do now is to create a game around it. That's what I did, and you're welcome to play that game by downloading it from its home on https://notklaatu.itch.io/unveil[itch.io] or from its source repository on https://git.nixnet.xyz/klaatu/unveil[git.nixnet.xyz]. Enjoy!