A Pac Man clone written in Go (with emojis!)

Overview

Pac Go

A Pac Man clone written in Go (with emojis!)

screenshot

Introduction

Welcome to Pac Go! This project is a tutorial to introduce people to the Go programming language.

Why a new tutorial?

We have a lot of great tutorials out there, but the whole idea about this tutorial is to make something different and fun while learning Go. Yes, we need to know about APIs and CRUDs on a daily basis, but while tackling something new, why not making a game instead?

Go is one of the languages that is known for making programming fun again, and what could be more fun than writing a game? Interested? Great, let's move on!

We will be writing a Pac Man clone for the terminal. While writing a game you are exposed to several interesting requirements that make a very good ground for exploring many features of the language, including input, output, functions, pointers, concurrency and a bit of math.

You also learn a bit more about the terminal and its magical escape sequences.

Conference Talks

If you want to have a look at the tutorial in a talk format before trying (about 25 minutes), try one of the links below:

  1. Google Cloud Next UK '19, London, UK (November, 21st 2019)
  2. London Gophers, London, UK
  3. GoWayFest 3.0, Minsk, Belarus
  4. GothamGo 2019, New York City, NY, USA

Contributing

This project is open source under the MIT license, which means you are basically free to do whatever you want with it, just give me the proper credits. :)

If you want to contribute just raise an issue and/or submit a pull request.

If you are looking for inspiration you may browse the open issues or have a look at the TODO list.

Everything on the TODO list should be planned as a new step on the tutorial unless otherwise noted.

License

See LICENSE.

Contacting the Author

If you have any questions, please reach out to me at [email protected]. I'm also on Twitter as @danicat83.

Getting Started

Pre-requisites

It's recommended to have:

  • Basic understanding on how programming languages work, as we won't be covering the basics
  • Basic terminal knowledge (know how to use command line applications)

Of course, if you don't have the above, but is a curious spirit and want to try anyway, please feel free to do so.

Compatibility Warning!!!

This tutorial has been tested on both Linux and Mac OS X environments. For Windows environments you may need to install a terminal emulator, like Git BASH.

Please beware that since this code relies on the terminal to render the game it can produce different results for different configurations.

If you have an issue feel free to raise it so we can find a proper solution, naming both your OS and terminal names and versions.

Note: It is a known issue that the terminal window on VS Code doesn't render the game correctly at this moment.

Setup

In order to start, make sure you have Go installed in your system.

If you don't, please follow the instructions in golang.org

How to use this tutorial

In every step, including step 0 (this one), we will describe the task in the README.md file followed by the code that does it and an explanation of how it works.

Every step is located in its separate folder except for this one. Look for the folders stepXX for any given step.

We will be editing a file called main.go. All the code in this tutorial will reside in this file. A proper program would usually have multiple source code files, but for the sake of simplicity we are keeping this program limited to one source.

The README.md for each step will explain the intent and show the modifications needed to proceed. You should make them in your own main.go file.

You can also use the main.go in step 00 as a starting point and modify it incrementally when progressing through the steps.

If you get lost, every step has its own main.go file with the changes to that step already applied. That also means that if you want to fast track to a given step you can start with the main.go from the previous step.

Step 00: Hello (Game) World

We are going to start by laying the ground a skeleton of what a game program looks like.

Pick a directory to be your work dir (e.g., tutorial under your home folder) and create a file called main.go with the content below.

Note: Alternatively you can just clone this repository and edit the main.go file on its root.

package main

import "fmt"

func main() {
    // initialize game

    // load resources

    // game loop
    for {
        // update screen

        // process input

        // process movement

        // process collisions

        // check game over

        // Temp: break infinite loop
        fmt.Println("Hello, Pac Go!")
        break

        // repeat
    }
}

Running your first Go program

Now that we have a main.go file (.go is the file extension for Go source code), you can run it by using the command line tool go. Just type:

$ go run main.go
Hello, Pac Go!

That's how we run a single file in Go. You can also build it as an executable program with the command go build. If you run go build it will compile the files in the current directory in a binary with the name of the directory. Then you can run it as a regular program, for example:

$ go build
$ ./pacgo
Hello, Pac Go!

For the purposes of this tutorial we are using only a single file of code (main.go), so you may use either go build and run the command or just go run main.go as it does both automatically.

Understanding the program

Now let's have a close look of what the program does.

First line is the package name. Every valid Go program must have one of these. Also, if you want to make a runnable program you must have a package called main and a main function, which will be the entry point of the program.

We are creating an executable program so we are using package main on the top of the file.

Next are the import statements. You use those to make code in other packages accessible to your program.

Finally the main function. You define function in Go with the keyword func followed by its name, its parameters in between a pair of parentheses, followed by the return value and finally the function body, which is contained in a pair of curly brackets {}. For example:

func main() {
    // I'm a function body
}

This is a function called main which takes no parameters and returns nothing. That's the proper definition of a main function in Go, as opposed to the definitions in other languages where the main function may take the command line arguments and/or return an integer value.

We have different ways to deal with the command line arguments and return values in Go, which we will see in Step 08.

In the game main function we have some comments (any text after // is a comment) that are acting as placeholders for the actual game code. We'll use those to drive each modification in an orderly way.

The most important concept in a game is what is called the game loop. That's basically an infinite loop where all the game mechanics are processed.

A loop in Go is defined with the keyword for. The for loop can have an initializer, a condition and a post processing step, but all of those are optional. If you omit all you have a loop that never ends:

for {
    // I never end
}

We can exit an infinite loop with a break statement. We are using it in the sample code to end the infinite loop after printing "Hello, Pac Go!" with the Println function from the fmt package (comments omitted for brevity):

func main() {
    for {
        fmt.Println("Hello, Pac Go!")
        break
    }
}

Of course, in this case the infinite loop with a non-conditional break is pointless, but it will make sense in the next steps!

Congratulations, step 00 is complete!

Take me to step 01!

Issues
  • Some race-condition may not get addressed,I am not sure

    Some race-condition may not get addressed,I am not sure

    ` var pillTimer *time.Timer var pillMx sync.Mutex

    func processPill() { pillMx.Lock() updateGhosts(ghosts, GhostStatusBlue) if pillTimer != nil { pillTimer.Stop() } pillTimer = time.NewTimer(time.Second * cfg.PillDurationSecs) pillMx.Unlock() <-pillTimer.C /gorotine a gets here,next to execute is pillMx.Lock(),but goroutine b gets control(the lock is free now.)execute from the beginning of the funtion to this line,blocked by pillTimer channel.however gorotine a now get the lock,it continues ,in this case,will ghosts get normal?/ pillMx.Lock() pillTimer.Stop() updateGhosts(ghosts, GhostStatusNormal) pillMx.Unlock() }

    opened by AlanYeg 2
  • subtle concurrency issue in step 6

    subtle concurrency issue in step 6

    In step 6 channels are introduced for processing the user input in the game loop. At the end of the step a delay with sleep is added to the game loop. This introduces the situation, that in each run through the loop the ghosts are moving and in some runs through the loop the player is moving. If before the loop both are side by side and in the loop both are moving in the opposite direction towards each other, the player can survive the hit of the ghost. The issue is the simultaneous use of a select from a channel and a sleep. The fix for this is to use a ticker for the delay and not a sleep. the ticker needs to write to the same channel as the player input. This way the for loop will process only one of this state changes of the game at a time. I find it important to mention this subtle concurrency issue in the presentation, so students learn correct use of concurrency.

    opened by rpoe 2
  • go mod files are missing

    go mod files are missing

    For the latest go version libraries need to have a go mod file for dependency management. Found by calling in directory step02 go run main.go output: main.go:10:2: no required module provides package github.com/danicat/simpleansi: go.mod file not found in current directory or any parent directory; see 'go help modules'

    opened by rpoe 2
  • Turning this awesome project into a Google Codelab format

    Turning this awesome project into a Google Codelab format

    I would like to request if we can turn this awesome repo in to Codelab,

    As we have most the steps defined in MD, that shouldn't be alot of work.

    What do you think @danicat , If yes, I would be more than happy to do it :D

    opened by nasirhm 5
  • Please include screenshots of the game in the README!

    Please include screenshots of the game in the README!

    Hi! Your project seems very cool and all, but without any screenshots of the game in the README, I don't see how people could trust that your game works and that it has emojis etc ;-) Thanks!

    opened by Naereen 5
  • runtime error: index out of range when makeMove()

    runtime error: index out of range when makeMove()

    Environment : go version go1.16.2 linux/amd64 In wsl2 Ubuntu20.04

    In ##step03##, when I move the 'P' to this position, the program panics an error like the title. image

    I print the logs in function makeMove()

           rest code
           ----
    	case "LEFT":
    		newCol = newCol - 1
    		if newCol < 0 {
    			newCol = len(maze[0]) - 1
    		}
    	}
    	log.Println("len of maze: ", len(maze))
    	log.Println("len of maze[0]: ", len(maze[0]))
    	log.Println("newRow: %d\n", newRow)
    	log.Println("newCol: %d\n", newCol)
    	if maze[newRow][newCol] == '#' {          //This is line 124
    		newRow = oldRow
    		newCol = oldCol
    	}
        ----
        rest code
    

    then It prints the message below

    2021/11/09 17:48:25 len of maze:  24
    2021/11/09 17:48:25 len of maze[0]:  28
    2021/11/09 17:48:25 newRow: %d
     11
    2021/11/09 17:48:25 newCol: %d
     27
    panic: runtime error: index out of range [27] with length 22
    
    goroutine 1 [running]:
    main.makeMove(0xb, 0x0, 0x4e3d37, 0x4, 0xb, 0x0)
            /mnt/e/github/pacgodemo/main.go:124 +0x37c
    main.movePlayer(...)
            /mnt/e/github/pacgodemo/main.go:135
    main.main()
            /mnt/e/github/pacgodemo/main.go:183 +0x145
    exit status 2
    

    I don't know why the length is 22. Anyone else can help me? Thanks

    opened by CaoGongHui 2
  • Works on which terminal

    Works on which terminal

    What environment is necessary to make this work? I tried various terminals, with varying results, but none of them will run this game (go-running the main.go from Step 9).

    • go version go1.13.1 linux/amd64
    • tried MATE Terminal (colourful, no movement, no player/ghosts), Sakura (similar), xvt (instant game over, no nice graphics), alacritty (flashes, no black & white graphics).
    help wanted 
    opened by pepa65 2
  • redundant cursor moving op

    redundant cursor moving op

    in step 02, you clear screen and move the cursor to (0, 0):

    func clearScreen() {
        fmt.Printf("\x1b[2J")
        moveCursor(0, 0)
    }
    

    But the escape sequence ^[2J should move the cursor to (0, 0) itself, so we don't have to explicitly move the cursor. Check here.

    opened by EtoDemerzel0427 2
  • fix negative lives bug

    fix negative lives bug

    If two ghosts kill you at the same time you can get trapped in the game with negative lives. This code fixes it for steps 08 to x. Previous steps were fixed in an earlier PR.

    opened by danicat 0
  • refactor for cloud next uk 19

    refactor for cloud next uk 19

    • Makes spelling consistent between steps
    • Renames init -> initialise
    • Remove Player and Ghost types, create a new sprite type
    • Makes all types unexported (lowercase)
    • Uses github.com/danicat/simpleansi for drawing
    • Add text about power up pill placeholder
    • Makes maze01.txt and config.json consistent between steps
    opened by danicat 0
  • Refactoring - Printf to Print/Println, Fatalf to Fatalln

    Refactoring - Printf to Print/Println, Fatalf to Fatalln

    Some Printf calls didn't use any formatting, so replaced them Calls with \n were replaced with *ln variants Calls with simple %v were replaced with Print/Println function with varargs, which concatenates strings automatically Fixed comment location

    opened by RunninglVlan 0
  • Adding multi lives support

    Adding multi lives support

    • Adds step10 (multiple lives support). Was not sure if you wanted the step count to match the TODO readme or not. I can change to step9 if needed. • Added an initialPosition variable to use for position reset after ghost collision. • update scoreboard to display lives as emojis instead of an integer. • added "GAME OVER" text to end of game • README for step10

    opened by jstiehl 0
  • Some race-condition may not get addressed,I am not sure

    Some race-condition may not get addressed,I am not sure

    ` var pillTimer *time.Timer var pillMx sync.Mutex

    func processPill() { pillMx.Lock() updateGhosts(ghosts, GhostStatusBlue) if pillTimer != nil { pillTimer.Stop() } pillTimer = time.NewTimer(time.Second * cfg.PillDurationSecs) pillMx.Unlock() <-pillTimer.C /gorotine a gets here,next to execute is pillMx.Lock(),but goroutine b gets control(the lock is free now.)execute from the beginning of the funtion to this line,blocked by pillTimer channel.however gorotine a now get the lock,it continues ,in this case,will ghosts get normal?/ pillMx.Lock() pillTimer.Stop() updateGhosts(ghosts, GhostStatusNormal) pillMx.Unlock() }

    opened by AlanYeg 2
  • subtle concurrency issue in step 6

    subtle concurrency issue in step 6

    In step 6 channels are introduced for processing the user input in the game loop. At the end of the step a delay with sleep is added to the game loop. This introduces the situation, that in each run through the loop the ghosts are moving and in some runs through the loop the player is moving. If before the loop both are side by side and in the loop both are moving in the opposite direction towards each other, the player can survive the hit of the ghost. The issue is the simultaneous use of a select from a channel and a sleep. The fix for this is to use a ticker for the delay and not a sleep. the ticker needs to write to the same channel as the player input. This way the for loop will process only one of this state changes of the game at a time. I find it important to mention this subtle concurrency issue in the presentation, so students learn correct use of concurrency.

    opened by rpoe 2
  • go mod files are missing

    go mod files are missing

    For the latest go version libraries need to have a go mod file for dependency management. Found by calling in directory step02 go run main.go output: main.go:10:2: no required module provides package github.com/danicat/simpleansi: go.mod file not found in current directory or any parent directory; see 'go help modules'

    opened by rpoe 2
Owner
Daniela Petruzalek
Executive Director at JPMC, Google Developer Expert - Go & GCP, speaker, blogger and cat lover =^.^=
Daniela Petruzalek
An attempt to clone the mechanics from the game Majesty.

An attempt to clone the mechanics from the game Majesty. Basically, an indirect control kingdom building simulation.

Jeremy Cerise 0 Jan 4, 2022
Engo is an open-source 2D game engine written in Go.

Engo A cross-platform game engine written in Go following an interpretation of the Entity Component System paradigm. Engo is currently compilable for

Engo 1.5k Jun 26, 2022
NES emulator, written in Go

Fergulator This is an NES emulator, written in Go. It's fairly new and very much a work in progress, so not all games run yet and not all features are

Scott Ferguson 599 Jun 15, 2022
A program written in Go that plays Go

Gongo - a Go player written in Go Author: Brian Slesinsky Last update: December 6, 2009 This is a simple computer program that plays Go, but not very

Brian Slesinsky 43 Aug 14, 2021
A ZX Spectrum Emulator written in Go

GoSpeccy - An evolving ZX Spectrum 48k Emulator GoSpeccy is a free ZX Spectrum (Speccy for friends) emulator written in Go. Quick start Installing and

Andrea Fazzi 122 Apr 4, 2022
The classic game Pong, written in Go

Pong Pong made in Golang Code is rough and needs to be cleaned up Warning: Many features such as collision detection have been hacked in and should be

Laurence Armstrong 17 Nov 2, 2021
Invincible AI written in Go for Tic Tac Toe game.

Tic-Tac-Toe-AI Invincible AI based on Minimax algorithm, written in Go for Tic Tac Toe game. Installation & Test First, install repository git clone h

Anıl Mısırlıoğlu 12 May 9, 2021
A chess engine written in golang

Weasel Art graciously provided by Alex Table of Contents: About Installing and Compiling from Source Contributing License About Weasel is an 0x88 and

WeaselChess Club 16 Apr 18, 2022
Currently in beta testing. A chess engine written in golang

Weasel Art graciously provided by Alex Table of Contents: About Installing and Compiling from Source Contributing License About Weasel is an 0x88 and

Weasel 16 Apr 18, 2022
Mettaur is GBA emulator written in golang.

Mettaur Mettaur is GBA emulator written in golang. Warning: This emulator is WIP, so many ROMs don't work correctly now. Run Please download latest bi

Akatsuki 424 Jun 15, 2022
A GameBoy emulator written in Go

gogoboy A GameBoy emulator written in Go About this project This project is a proof of concept of building emulators with test driven development. I'v

Daniela Petruzalek 9 Dec 23, 2021
Magia is GBA emulator written in golang.

magia is GBA emulator written in golang.

Akatsuki 425 Jun 21, 2022
F1 Game Telemetry Client written in Go (no dependency)

F1 Game Telemetry Client in Go Telemetry client for F1 Game, written in Go. Currently, supported only the UDP 2020 format. Features Event System Rich

Anıl Mısırlıoğlu 57 Jun 9, 2022
Fab.io is a lightweight game backend framework written in Go (Golang).

Fab.io is a lightweight real-time game backend framework written in Go (Golang).

null 20 Jun 20, 2022
A toy GameBoy Color emulator written in golang.

?? worldwide 日本語のドキュメントはこちら GameBoyColor emulator written in golang. This emulator can play a lot of ROMs work without problems and has many features.

Akatsuki 562 Jun 15, 2022
A Chip8 emulator written in Go

A Chip8 Emulator in Go chip8.go is a simple Chip8 emulator, compliant with the technical standard laid out in the Cowgod's Manual. Graphics and sound

Ambertide 36 Jun 6, 2022
Simple word guessing game written in golang.

Word Guessing Game Simple word guessing game written in golang. successTexts := []string{ "You guessed right! ????????", "Correct! ✅", "Horray!

Mahmudul Hasan 1 Oct 19, 2021
Tetra3D is a 3D software renderer written in Go and Ebiten and made for games.

Tetra3D Tetra3D Docs Support If you want to support development, feel free to check out my itch.io / Steam / Patreon. I also have a Discord server her

SolarLune 242 Jun 21, 2022
CHIP-8 Emulator written in Go

dP oo 88 .d8888b. 88d888b. dP 88d888b. 88d888b. dP dP 88' `"" 88' `88 88 88' `88 88' `88 88 88 88. ... 88 88 88 88. .88

m0x 2 Nov 26, 2021