package conway import ( "bytes" "encoding/base64" "encoding/json" "fmt" "image/png" "log" "math/rand" "net/http" "net/url" "strconv" "time" ) type WebGameParams struct { Random bool Height int Width int Xmul int Ymul int } type WebGameState struct { State *GameState `json:"state"` Img string `json:"img"` } var jQueryMinJs = []byte("") func init() { rand.Seed(time.Now().UTC().UnixNano()) jqmin, err := base64.StdEncoding.DecodeString(JQUERY_MIN_JS) if err != nil { log.Fatal("Failed to decode internal jquery.min.js") } jQueryMinJs = append(jQueryMinJs, jqmin...) } func NewWebGameParams(q url.Values) *WebGameParams { params := &WebGameParams{ Height: 50, Width: 50, Xmul: 10, Ymul: 10, } if len(q.Get("random")) > 0 { params.Random = true } h := q.Get("h") if len(h) > 0 { if i, err := strconv.Atoi(h); err == nil { params.Height = i } } w := q.Get("w") if len(w) > 0 { if i, err := strconv.Atoi(w); err == nil { params.Width = i } } xm := q.Get("xm") if len(xm) > 0 { if i, err := strconv.Atoi(xm); err == nil { params.Xmul = i } } ym := q.Get("ym") if len(ym) > 0 { if i, err := strconv.Atoi(ym); err == nil { params.Ymul = i } } return params } func handle405(method, uri string, w http.ResponseWriter) { w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusMethodNotAllowed) w.Write([]byte(fmt.Sprintf("Nope. %v not allowed at %v\n", method, uri))) } func handle500(err error, w http.ResponseWriter) { log.Println("ERROR:", err) w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(fmt.Sprintf("We exploded!: %v\n", err))) } func HandleWebGameRoot(w http.ResponseWriter, req *http.Request) { if req.Method != "GET" { handle405(req.Method, req.RequestURI, w) return } w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusOK) w.Write([]byte(GAME_OF_LIFE_INDEX_HTML)) return } func HandleWebGameStatic(w http.ResponseWriter, req *http.Request) { if req.Method != "GET" { handle405(req.Method, req.RequestURI, w) return } if req.URL.Path == "/static/normalize.css" { w.Header().Set("Content-Type", "text/css") w.WriteHeader(http.StatusOK) w.Write([]byte(NORMALIZE_CSS)) return } if req.URL.Path == "/static/jquery.min.js" { w.Header().Set("Content-Type", "text/javascript") w.WriteHeader(http.StatusOK) w.Write(jQueryMinJs) return } w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusNotFound) w.Write([]byte(fmt.Sprintf("Don't have: %v\n", req.URL.Path))) } func HandleWebGameState(w http.ResponseWriter, req *http.Request) { if req.Method != "GET" { handle405(req.Method, req.RequestURI, w) return } q := req.URL.Query() params := NewWebGameParams(q) if params.Random { game := NewGameOfLife(params.Height, params.Width) err := game.ImportRandomState() if err != nil { handle500(err, w) return } img, err := game.Image(params.Xmul, params.Ymul) if err != nil { handle500(err, w) return } log.Println("Serving random state image.") var pngBuf bytes.Buffer err = png.Encode(&pngBuf, img) if err != nil { handle500(err, w) } imgB64 := base64.StdEncoding.EncodeToString(pngBuf.Bytes()) webGameState := &WebGameState{State: game.State, Img: imgB64} wgsJson, err := json.Marshal(webGameState) if err != nil { handle500(err, w) } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write(wgsJson) return } rawState := q.Get("s") if len(rawState) < 1 { w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Missing query param \"s\".\n")) return } //state := &GameState{} //err := json.Unmarshal([]byte(rawState), state) //if err != nil { //w.Header().Set("Content-Type", "text/plain") //w.WriteHeader(http.StatusBadRequest) //w.Write([]byte(fmt.Sprintf("Invalid query param \"s\": %v\n", err))) //return //} w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte(`{"state":null,"img":""}`)) return } func RunWebGame(address string, height, width, sleepMs int, mutate bool) (int, error) { http.HandleFunc("/", HandleWebGameRoot) http.HandleFunc("/state", HandleWebGameState) http.HandleFunc("/static/", HandleWebGameStatic) fmt.Printf("Serving on %v\n", address) err := http.ListenAndServe(address, nil) if err != nil { return 1, err } return 0, nil }