Last active
April 13, 2020 05:16
-
-
Save gautamrege/be9117bb5196297cafba94fca796b7af to your computer and use it in GitHub Desktop.
Hangman - Part 4 - interfaces and testing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"flag" | |
"fmt" | |
"io/ioutil" | |
"log" | |
"net/http" | |
"strings" | |
) | |
const MAX_CHANCES int = 8 | |
var dev = flag.Bool("dev", false, "Development environment") | |
type Hangman struct{} | |
type Gamer interface { | |
RenderGame([]string, map[string]bool) error | |
GetInput() string | |
} | |
func (h *Hangman) RenderGame(placeholder []string, entries map[string]bool) error { | |
fmt.Println(placeholder) | |
fmt.Printf("Chances left: %d\n", MAX_CHANCES-len(entries)) | |
fmt.Printf("Guesses: %s\n", get_keys(entries)) | |
fmt.Printf("Guess a letter or the word: ") | |
return nil | |
} | |
func (h *Hangman) GetInput() string { | |
str := "" | |
fmt.Scanln(&str) | |
str = strings.TrimSuffix(str, "\n") | |
return str | |
} | |
func get_keys(entries map[string]bool) []string { | |
keys := []string{} | |
for k, _ := range entries { | |
keys = append(keys, k) | |
} | |
return keys | |
} | |
func play(h Gamer, word string) bool { | |
entries := map[string]bool{} | |
placeholder := make([]string, len(word), len(word)) | |
// initialize to '_' | |
for i := range word { | |
placeholder[i] = "_" | |
} | |
for { | |
// evaluate a loss! | |
if len(entries) == MAX_CHANCES { | |
return false | |
} | |
// evaluate a win! | |
if strings.Join(placeholder, "") == word { | |
return true | |
} | |
h.RenderGame(placeholder, entries) | |
str := h.GetInput() | |
if str == word { | |
return true | |
} else if str == "" { | |
// someone pressed enter direclty! | |
continue | |
} else if len(str) > 1 { | |
entries[str] = true | |
continue | |
} | |
input := rune(str[0]) // take input and convert to rune (similar to char) | |
// evalute the entry | |
found := false | |
for i, e := range word { | |
if input == e { | |
placeholder[i] = string(input) | |
found = true | |
} | |
} | |
if !found { | |
entries[string(str)] = true | |
} | |
} | |
// should never come here! | |
return false | |
} | |
func get_word() string { | |
res, err := http.Get("https://random-word-api.herokuapp.com/word?number=5") | |
if err != nil { | |
log.Fatal(err) | |
} | |
blob, err := ioutil.ReadAll(res.Body) | |
res.Body.Close() | |
if err != nil { | |
log.Fatal(err) | |
} | |
var words []string | |
err = json.Unmarshal(blob, &words) | |
if err != nil { | |
fmt.Println(err) | |
return "elephant" | |
} | |
for _, word := range words { | |
if len(word) > 4 && len(word) < 11 { | |
// return a word between 5 and 10 characters | |
return string(word) | |
} | |
} | |
return "elephant" // default | |
} | |
func main() { | |
flag.Parse() | |
word := "elephant" | |
if *dev == false { | |
word = get_word() | |
} | |
game := &Hangman{} | |
if play(game, word) == true { | |
fmt.Println("You win! You've saved yourself from a hanging") | |
} else { | |
fmt.Println("Damn! You're hanged!!") | |
fmt.Println("Word was: ", word) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"math/rand" | |
"testing" | |
"time" | |
"github.com/stretchr/testify/assert" | |
"github.com/stretchr/testify/mock" | |
"github.com/stretchr/testify/suite" | |
) | |
type MockGamer struct { | |
mock.Mock | |
} | |
func (m *MockGamer) RenderGame(placeholder []string, entries map[string]bool) error { | |
args := m.Called(placeholder, entries) | |
return args.Error(0) | |
} | |
func (m *MockGamer) GetInput() (str string) { | |
args := m.Called() | |
randomStringFunc, ok := args.Get(0).(func(charset string, length int) string) | |
if ok { | |
str = randomStringFunc(args.String(1), args.Int(2)) | |
return | |
} | |
return args.String(0) | |
} | |
type HangmanTestSuite struct { | |
suite.Suite | |
word string | |
mockGamer *MockGamer | |
} | |
func (suite *HangmanTestSuite) SetupTest() { | |
rand.Seed(time.Now().UnixNano()) | |
suite.mockGamer = &MockGamer{} | |
suite.word = "elephant" | |
} | |
func TestHangmanTestSuite(t *testing.T) { | |
suite.Run(t, new(HangmanTestSuite)) | |
} | |
func (suite *HangmanTestSuite) TestPlaySuccess() { | |
suite.mockGamer.On("RenderGame", mock.Anything, mock.Anything).Return(nil) | |
// whenever GetInput will be called | |
// it will return a random character from suite.word => "elephant" | |
suite.mockGamer.On("GetInput").Return(randomString, suite.word, 1) | |
result := play(suite.mockGamer, suite.word) | |
assert.Equal(suite.T(), result, true) | |
} | |
func (suite *HangmanTestSuite) TestPlayWhenUserOutOfChances() { | |
suite.mockGamer.On("RenderGame", mock.Anything, mock.Anything).Return(nil) | |
// whenever GetInput will be called | |
// it will return a random character from cdxzoafbnqj | |
// after sometime user will get out of chances | |
suite.mockGamer.On("GetInput").Return(randomString, "cdxzoafbnqj", 1) | |
result := play(suite.mockGamer, suite.word) | |
assert.Equal(suite.T(), result, false) | |
} | |
func (suite *HangmanTestSuite) TestPlayWhenUserEntersCompleteWord() { | |
suite.mockGamer.On("RenderGame", mock.Anything, mock.Anything).Return(nil) | |
// whenever GetInput will be called | |
// it will return the complete word => elephant | |
// user will be saved from hanging in the first attempt | |
suite.mockGamer.On("GetInput").Return(suite.word) | |
result := play(suite.mockGamer, suite.word) | |
assert.Equal(suite.T(), result, true) | |
} | |
func randomString(charset string, length int) string { | |
index := randomInt(0, len(charset)) | |
return string(charset[index]) | |
} | |
func randomInt(min, max int) int { | |
return min + rand.Intn(max-min) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment