step05

command
v0.0.0-...-54c63c2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 22, 2024 License: MIT Imports: 7 Imported by: 0

README

Step 05: Game over man, game over!

In this lesson you will learn how to:

  • Use the fallthrough statement in switch blocks
  • Work with slices

Overview

We are almost there. We have both player movement and ghost movement. But our ghosts are still inoffensive.

It's time to add some danger to this game. Also, with great risks should come great rewards, so we'll also be tackling the game win condition, by clearing the board of all its dots.

Task 01: Preparation

For the game win condition we need to keep track of how many dots we have on the board, and declare win when this number is zero.

We will need a mechanic to remove the dots from the board once the player stands over them. We will also keep track of a score to show the player.

For the game over scenario, we will be giving the player one life and when a ghost hits them this life is zeroed. We then test for zero lives in the game loop to terminate the game. (It should be pretty straightforward to add support for multiple lives, but we will do this at a later step).

Add the following global variables to track all of the above:

var score int
var numDots int
var lives = 1

Next, we need to initialize the numDots variable in loadMaze. We just need a new case in the switch that handles the . character:

for row, line := range maze {
    for col, char := range line {
        switch char {
        case 'P':
            player = sprite{row, col}
        case 'G':
            ghosts = append(ghosts, &sprite{row, col})
        case '.':
            numDots++
        }
    }
}

Now we need to update the printScreen function to print the dots again. This is an interesting case for the fallthrough statement:

func printScreen() {
    simpleansi.ClearScreen()
    for _, line := range maze {
        for _, chr := range line {
            switch chr {
            case '#':
                fallthrough
            case '.':
                fmt.Printf("%c", chr)
            default:
                fmt.Print(" ")
            }
        }
        fmt.Println()
    }
    // rest of the function omitted for brevity...
}

Finally, at the end of the printScreen function let's add our score and lives panel:

func printScreen() {
    // code omitted...

    // print score
    simpleansi.MoveCursor(len(maze)+1, 0)
    fmt.Println("Score:", score, "\tLives:", lives)
}

Task 02: Game over

To process game over is pretty straightforward. At any given moment in time, we are killing the player if they are in the same spot as a ghost. We will add the code that detects this to the game loop. We are also modifying the game quit condition adding lives <= 0 and numDots == 0:

// game loop
for {
    // code ommited...

    // process collisions
    for _, g := range ghosts {
        if player == *g {
            lives = 0
        }
    }

    // check game over
    if input == "ESC" || numDots == 0 || lives <= 0 {
        break
    }

    // repeat
}

Please note that the more verbose way of checking the player position is player.row == g.row && player.col == g.col, but since both player and ghost are sprites they can use a simple comparison player == *g. We still need to dereference g because we can't compare pointer and non pointer types.

Task 03: Game win

We are now just missing the code to remove the dots from the game and increment the score.

We will add this code to the movePlayer function:

func movePlayer(dir string) {
    player.row, player.col = makeMove(player.row, player.col, dir)
    switch maze[player.row][player.col] {
    case '.':
        numDots--
        score++
        // Remove dot from the maze
        maze[player.row] = maze[player.row][0:player.col] + " " + maze[player.row][player.col+1:]
    }
}

The code above works as follows: first we make the movement. Then we check which character is in the same spot as the player. If it is a dot, we decrement the total number of dots (numDots), increment the score and remove the dot from the board.

It's worthwhile to mention that strings in Go are immutable. We couldn't simply assign a space to a given position in a string. It wouldn't work.

Hence, we are using here a trick by creating a new string composed by two slices of the original string. The two slices together make the exact same original string except for one position, that we replace with a space.

For more information about slices, please see here.

Now we have both game win and game over conditions. Try building a map with just a couple of dots and test the game win. Hit a ghost to test the game over. We are making progress!

(Tip: the maze01.txt at the step05 folder has only 3 dots.)

Take me to step 06!

Documentation

The Go Gopher

There is no documentation for this package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL