Giter Club home page Giter Club logo

game-achievements-system's Introduction

intro

this is my solution to a coding challenge i received when i applied for a backend engineer position.

the project can be summarized as "an achievements system for a fictional online game". please see SPEC.md for challenge details.

i decided to implement a restful api in go, so this is a web server connected to a database that exposes several endpoints which accept GET, POST, PUT and DELETE requests in order to create, read, update and delete records

project components:

api

endpoint GET POST PUT DELETE notes
/achievements get all achievements
create a new achievement
fields: slug name desc img
/achievements/{id:} get, update or delete an achievement
/achievements/{id:}/members get members who have an achievement
/members get all members
create a new member
fields: name img
/members/{id:} get, update or delete a member
/members/{id:}/achievements get all achievements of a member
/members/{id:}/teams get all teams of a member
/members/{id:}/teams/{id:} a member joins - or - leaves a team
/teams get all teams
create a new team
fields: name img
/teams/{id:} get, update or delete a team
/teams/{id:}/members get all members of a team
/teams/{id:}/members/{id:} add a member to a team
remove a member from a team
/games get all games
create a new game
/games/{id:} close a game and run achievement logic
/games/{id:}/stats get all stats for a game
/games/{id:}/teams get all teams that joined a game
/games/{id:}/teams/{id:} add a team to a game;
team must contain 3-5 members;
/games/{id:}/teams/{id:}/stats get game stats for a team
/games/{id:}/members get all members of a game
/games/{id:}/winners get all winning members of a game
/games/{id:}/members/{id:}/stats get or update game stats for a member;
fields: num_attacks num_hits amount_damage num_kills instant_kills num_assists num_spells spells_damage

project files

achievements.go
  • individual functions that are run at the end of each game to check if a player qualifies for an achievement
config.go
  • program configuration settings: server listening address, database file name, etc
game.go
  • contains game related logic: adding teams to games, setting winners at the end of a game, etc
handlers.go
  • functions responsible for each endpoint and request type
main.go
  • initializes other program components and launches server
main_test.go
  • unit tests and sample data driver
model.go
  • data structures and generalized functions for manipulating database records
response.go
  • errors and json response encoding
routes.go
  • endpoints, http request methods

running

before running the program, make sure to install the dependencies, this only has to be done once:

go get github.com/gorilla/mux

go get github.com/jinzhu/gorm

go get github.com/jinzhu/gorm/dialects/sqlite

to build and run:

go build && ./game-achievements-system

or just run without building:

go run .

program should print server ready at: 0.0.0.0:4242

testing

file main_test.go contains unit tests and also acts as a driver.

the test scenario is simple:

  • create the four default achievements
  • create 10 sample members (players)
  • create 4 sample teams
  • start 2 games
  • update member stats
  • close games
  • verify that server awarded correct wins and achievements to qualifying members

to run the tests:

go test -v

the test will fail if the database file is already populated with data, if that happens just remove database.sqlite and re-run the test - or - you could edit config.go and set databaseFile = ":memory:"

adding new achievements

to address SPEC.md requirement #7, "add a new statistic and create a new achievement ..":

to add a simple statistic like "number of collected coins" during a game we would simply add it to our Stat object which is defined in models.go; it might look like this:

type Stat struct {
	gorm.Model
	GameID       uint `json:"game_id"`
	TeamID       uint `json:"team_id"`
	MemberID     uint `json:"member_id"`
	NumAttacks   uint `json:"num_attacks"`
	NumHits      uint `json:"num_hits"`
	AmountDamage uint `json:"amount_damage"`
	NumKills     uint `json:"num_kills"`
	InstantKills uint `json:"instant_kills"`
	NumAssists   uint `json:"num_assists"`
	NumSpells    uint `json:"num_spells"`
	SpellsDamage uint `json:"spells_damage"`
	NumCoins     uint `json:"num_coins"`		// <<--- we added a new statistic
	IsWinner     bool `json:"is_winner"`
}

next, we want to add the achievement to the database; here is one way of doing that:

curl -X POST 0.0.0.0:4242/achievements --data '{"name": "1UP", "slug": "oneup", "desc": "You have collected 100 or more coins during a single game! Wow!", "img": "https://www.pngfind.com/pngs/m/22-226955_mario-mushroom-png-mario-1-up-mushroom-transparent.png"}'

if successful, the server replies "success":true, "code":201

{"success":true,"code":201,"message":"ok","result":{"ID":5,"CreatedAt":"2019-08-09T23:44:41.040203-07:00","UpdatedAt":"2019-08-09T23:44:41.040203-07:00","DeletedAt":null,"slug":"oneup","name":"1UP","desc":"You have collected 100 or more coins during a single game! Wow!","img":"https://www.pngfind.com/pngs/m/22-226955_mario-mushroom-png-mario-1-up-mushroom-transparent.png"}}

then, in achievements.go we want to add a function which will check if a member qualifies for the achievement; function signature should be func (Stat) bool it might look like this:

func isOneUpAward(stat Stat) bool {
	if stat.NumCoins >= 100 {
		return true
	}
	return false
}

we also have to "register" the achievement by adding it to the asf list which is in achievements.go;

    asf = []typeAsf{
		{achievement: Achievement{}, slug: "sharpshooter", function: isSharpshooterAward},
		{achievement: Achievement{}, slug: "bruiser", function: isBruiserAward},
		{achievement: Achievement{}, slug: "veteran", function: isVeteranAward},
		{achievement: Achievement{}, slug: "bigwinner", function: isBigwinnerAward},
		{achievement: Achievement{}, slug: "oneup", function: isOneUpAward},		// <<--- 1UP new achievement
	}

it is important that the slug field matches the slug that we previously inserted into database.

last step is to restart the server. at the end of each game the new achievement will be awarded to qualifying members.

note:

we can create more interesting achievements by using sql capabilities of the database,

for example to check if a member qualifies for a "Win-Five-Games-In-A-Row" achievement:

func isWonLastFive(stat Stat) bool {
	lastFiveStats := []Stat{}
	db.Order("CreatedAt desc").Limit(5).Joins("JOIN games ON games.id = stats.game_id AND games.status = ? AND stats.member_id = ?", finishedGame, stat.MemberID).Find(&lastFiveStats)
	if len(lastFiveStats) == 5 {
		result := true
		for i := range lastFiveStats { result = result && lastFiveStats[i].IsWinner }
		return result
	}
	return false;
}

references

game-achievements-system's People

Contributors

asarandi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.