Start
This commit is contained in:
parent
95e6383f94
commit
105e28af36
|
@ -62,15 +62,8 @@ lives: %0.4v | score: %d | combo: %d
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
trial.SelectCard(0)
|
|
||||||
}
|
}
|
||||||
fmt.Printf("hand: %s\n", trial.ShowString())
|
fmt.Printf("hand: %s\n", trial.ShowString())
|
||||||
case "debug":
|
|
||||||
fmt.Println(trial)
|
|
||||||
case "exit", "quit", "q":
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.tuxpa.in/a/card_id/common/game"
|
||||||
|
"git.tuxpa.in/a/card_id/common/game/card"
|
||||||
|
"lukechampine.com/frand"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Engine struct {
|
||||||
|
dealer *card.Dealer
|
||||||
|
|
||||||
|
games map[string]*game.Game
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
dealer, err := card.NewDealer().ReadFromRoot(os.Getenv("CARD_DATA_DIR"))
|
||||||
|
if err != nil {
|
||||||
|
log.Panicln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e := &Engine{
|
||||||
|
dealer: dealer,
|
||||||
|
games: map[string]*game.Game{},
|
||||||
|
}
|
||||||
|
|
||||||
|
r := chi.NewRouter()
|
||||||
|
|
||||||
|
r.Use(middleware.RequestID)
|
||||||
|
r.Use(middleware.Logger)
|
||||||
|
r.Use(middleware.Recoverer)
|
||||||
|
r.Use(middleware.URLFormat)
|
||||||
|
r.Use(render.SetContentType(render.ContentTypeJSON))
|
||||||
|
|
||||||
|
r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte("OK"))
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Get("/ping", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte("pong"))
|
||||||
|
})
|
||||||
|
|
||||||
|
// RESTy routes for "instances" resource
|
||||||
|
r.Route("/game", func(r chi.Router) {
|
||||||
|
// r.Use(oauth.Authorize(os.Getenv("OAUTH_TOKEN"), nil))
|
||||||
|
r.Post("/new", e.CreateGame)
|
||||||
|
r.Route("/{instanceId}", func(r chi.Router) {
|
||||||
|
r.Use(e.GameCtx)
|
||||||
|
r.Route("/play", func(r chi.Router) {
|
||||||
|
r.Get("/click/{cardId}", nil)
|
||||||
|
})
|
||||||
|
r.Route("/debug", func(r chi.Router) {
|
||||||
|
r.Get("/info", e.GetGame)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
port := ":3333"
|
||||||
|
log.Println(port)
|
||||||
|
http.ListenAndServe(port, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GameCtx middleware is used to load an Game object from
|
||||||
|
// the URL parameters passed through as the request. In case
|
||||||
|
// the Game could not be found, we stop here and return a 404.
|
||||||
|
func (e *Engine) GameCtx(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var instance *game.Game
|
||||||
|
if instanceID := chi.URLParam(r, "instanceId"); instanceID != "" {
|
||||||
|
instance = e.games[instanceID]
|
||||||
|
}
|
||||||
|
if instance == nil {
|
||||||
|
render.Render(w, r, ErrNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx := context.WithValue(r.Context(), "instance", instance)
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) CreateGame(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id := strconv.Itoa(frand.Intn(0xfff))
|
||||||
|
e.games[id] = game.New("", e.dealer)
|
||||||
|
w.Write([]byte(id))
|
||||||
|
w.WriteHeader(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) GetGame(w http.ResponseWriter, r *http.Request) {
|
||||||
|
instance, ok := r.Context().Value("instance").(*game.Game)
|
||||||
|
if !ok {
|
||||||
|
render.Render(w, r, ErrNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := json.NewEncoder(w).Encode(instance)
|
||||||
|
if err != nil {
|
||||||
|
render.Render(w, r, ErrInvalidRequest(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrNotFound = &ErrResponse{HTTPStatusCode: 404, StatusText: "game not found."}
|
||||||
|
|
||||||
|
type ErrResponse struct {
|
||||||
|
Err error `json:"-"` // low-level runtime error
|
||||||
|
HTTPStatusCode int `json:"-"` // http response status code
|
||||||
|
|
||||||
|
StatusText string `json:"status"` // user-level status message
|
||||||
|
AppCode int64 `json:"code,omitempty"` // application-specific error code
|
||||||
|
ErrorText string `json:"error,omitempty"` // application-level error message, for debugging
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrResponse) Render(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
render.Status(r, e.HTTPStatusCode)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrInvalidRequest(err error) render.Renderer {
|
||||||
|
return &ErrResponse{
|
||||||
|
Err: err,
|
||||||
|
HTTPStatusCode: 400,
|
||||||
|
StatusText: "Invalid request.",
|
||||||
|
ErrorText: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrRender(err error) render.Renderer {
|
||||||
|
return &ErrResponse{
|
||||||
|
Err: err,
|
||||||
|
HTTPStatusCode: 422,
|
||||||
|
StatusText: "Error rendering response.",
|
||||||
|
ErrorText: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,11 @@
|
||||||
package game
|
package game
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.tuxpa.in/a/card_id/common/errs"
|
||||||
"git.tuxpa.in/a/card_id/common/game/card"
|
"git.tuxpa.in/a/card_id/common/game/card"
|
||||||
|
"git.tuxpa.in/a/card_id/common/game/score"
|
||||||
"lukechampine.com/frand"
|
"lukechampine.com/frand"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,15 +26,44 @@ type Game struct {
|
||||||
Dealer *card.Dealer `json:"-"`
|
Dealer *card.Dealer `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) New(user string, dealer *card.Dealer) *Game {
|
func New(user string, dealer *card.Dealer) *Game {
|
||||||
return &Game{
|
return &Game{
|
||||||
Dealer: dealer,
|
Dealer: dealer,
|
||||||
Player: user,
|
Player: user,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) Resolve(action string, args []string) {
|
func (g *Game) Resolve(action string, args []string) (resp any, err error) {
|
||||||
|
switch action {
|
||||||
|
case "click":
|
||||||
|
resp, err = g.ActionClick(args)
|
||||||
|
default:
|
||||||
|
err = errs.Invalid("cmd not found")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) ActionClick(args []string) (resp any, err error) {
|
||||||
|
if len(args) > 1 {
|
||||||
|
num, _ := strconv.Atoi(args[1])
|
||||||
|
_, err := g.Trial.SelectCard(num)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errs.Invalid("missing arg")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Tick() (err error) {
|
||||||
|
sc, lives := g.Trial.CheckSelection()
|
||||||
|
if sc != 0 {
|
||||||
|
g.Combo = g.Combo + 1
|
||||||
|
g.Score = g.Score + sc*score.DefaultTable.Ratio(g.Combo)*g.CurrentTrial
|
||||||
|
} else {
|
||||||
|
g.Combo = 0
|
||||||
|
}
|
||||||
|
g.Lives = g.Lives + lives
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) CreateTrial(pairs int, extra int) *Trial {
|
func (g *Game) CreateTrial(pairs int, extra int) *Trial {
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -3,11 +3,15 @@ module git.tuxpa.in/a/card_id
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/go-chi/chi/v5 v5.0.7
|
||||||
|
github.com/go-chi/oauth v0.0.0-20210913085627-d937e221b3ef
|
||||||
|
github.com/go-chi/render v1.0.1
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||||
lukechampine.com/frand v1.4.2
|
lukechampine.com/frand v1.4.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible // indirect
|
||||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect
|
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect
|
||||||
)
|
)
|
||||||
|
|
8
go.sum
8
go.sum
|
@ -1,5 +1,13 @@
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
||||||
|
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
|
||||||
|
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
|
github.com/go-chi/oauth v0.0.0-20210913085627-d937e221b3ef h1:lqU8HyH6bzhV+HHvgFaT2xBl19tcjs9F4UULmw3hTxc=
|
||||||
|
github.com/go-chi/oauth v0.0.0-20210913085627-d937e221b3ef/go.mod h1:eFAdB6Jo7GOKhl1PWiN2lKPxgFr7dBFkRrsz6S5IwOs=
|
||||||
|
github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
|
||||||
|
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
|
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
|
||||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
|
Loading…
Reference in New Issue