Skip to content

Instantly share code, notes, and snippets.

@gautamrege
Created April 13, 2020 05:11
Show Gist options
  • Save gautamrege/3688cea23ed1675a7b6d5f0bd7d84e0b to your computer and use it in GitHub Desktop.
Save gautamrege/3688cea23ed1675a7b6d5f0bd7d84e0b to your computer and use it in GitHub Desktop.
Hangman - Part 3 struct and receiver methods
package main
import (
"bufio"
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
"time"
)
var dev = flag.Bool("dev", false, "dev mode")
const (
GAME_PLAY = iota // untyped int constants
GAME_WIN
GAME_LOSE
GAME_TIMEOUT
GAME_ERR
)
type Hangman struct {
Entries map[string]bool
Placeholder []string
chances int
word string
duration time.Duration // game duration
input io.Reader
}
func get_keys(entries map[string]bool) (keys []string) {
for k, _ := range entries {
keys = append(keys, k)
}
return
}
func get_word() string {
if *dev {
return "elephant"
}
resp, err := http.Get("https://random-word-api.herokuapp.com/word?number=5")
if err != nil {
return "elephant"
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
var words []string
err = json.Unmarshal(body, &words)
if err != nil {
// handle error
return "elephant"
}
//fmt.Println(words)
for _, word := range words {
if len(word) > 4 && len(word) < 9 {
return word
}
}
return words[0]
}
func (h *Hangman) Render(status uint) {
switch status {
case GAME_PLAY:
fmt.Println(h.Placeholder) // render the placeholder
fmt.Printf("Chances: %d\n", h.chances) // render the chances left
fmt.Printf("Entries: %v\n", get_keys(h.Entries)) // show the letters or words guessed till now.
fmt.Printf("Guess a letter or the word: ")
case GAME_WIN:
fmt.Println("You win!!")
case GAME_LOSE:
fmt.Println("You're out of chances")
fmt.Println("Word was: ", h.word)
fmt.Println("Game Over! Try again")
case GAME_ERR:
fmt.Println("Something wrong with getting input")
case GAME_TIMEOUT:
fmt.Println("Timedout... too bad!")
}
}
func (h *Hangman) GetInput() (str string) {
str = ""
fmt.Scanln(&str)
return
}
func get_input(input io.Reader) (str string, err error) {
scanner := bufio.NewScanner(input)
if scanner.Scan() { // scan one line
str = scanner.Text()
return
}
err = scanner.Err()
return
}
func play(h *Hangman, result chan bool) {
for {
// evaluate a loss! If user guesses a wrong letter or the wrong word, they lose a chance.
userInput := strings.Join(h.Placeholder, "")
if h.chances == 0 && userInput != h.word {
h.Render(GAME_LOSE)
result <- false
return
}
// evaluate a win!
if userInput == h.word {
h.Render(GAME_WIN)
result <- true
return
}
// Console display
h.Render(GAME_PLAY)
// Addon validation: Allow only alpha!
// Addon validation: manage case!
str := h.GetInput()
// if len(str) > 2, compare the word with the str
if len(str) > 2 {
if str == h.word {
h.Render(GAME_WIN)
result <- true
return
} else {
// you lose a chance
h.Entries[str] = true
h.chances -= 1
continue
}
}
// compare and update entries, placeholder and chances.
_, ok := h.Entries[str]
if ok {
// key exists already; duplicate
continue
}
h.Entries[str] = true
// check if letter exists in the word!
found := false
for i, v := range h.word {
if str == string(v) {
h.Placeholder[i] = string(v)
found = true
}
}
if !found {
h.chances -= 1
}
}
}
func main() {
flag.Parse()
h := Hangman{
Entries: map[string]bool{},
Placeholder: []string{},
chances: 8,
duration: 2 * time.Minute,
}
h.word = get_word()
// get length of the word len(word)
// initialize slice with each element as "_"
for i := 0; i < len(h.word); i++ {
h.Placeholder = append(h.Placeholder, "_")
//placeholder[i] = "_"
}
t := time.NewTimer(h.duration)
result := make(chan bool)
go play(&h, result)
// Wait on either game to finish or timeout!
select {
case <-result:
case <-t.C:
h.Render(GAME_TIMEOUT)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment