add: testing to tictactoe rename: tictactoe add: tictactoe steps refactor: tictactoe const names refactor: tictactoe loop add: tictactoe bad switch example (fallthrough) refactor: tictactoe loop skin remove: tictactoe changable skin refactor: tictactoe all remove: tictactoe unnecessary base dir add: tictactoe slices add: tictactoe slices remove: tictactoe fallthrough rename: tictactoe slices 10 -> 09 update: loops skin tictactoe add: tictactoe randomization add: tictactoe infinite loop and labeled break refactor: tictactoe rand and infinite loop add: tictactoe buggy winning algo add: tictactoe more tests rename: tictactoe wrongPlay to wrongMove add: tictactoe even more tests fix: tictactoe rename: tictactoe waitForInput to wait add: tictactoe os.args gameSpeed remove: tictactoe unnecessary files rename: tictactoe game messages refactor: tictactoe main loop add: types and arrays
		
			
				
	
	
		
			167 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			167 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"bufio"
 | 
						|
	"fmt"
 | 
						|
	"os"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
func main() {
 | 
						|
	fmt.Println(`
 | 
						|
~~~~~~~~~~~~~~~
 | 
						|
  TIC~TAC~TOE 
 | 
						|
~~~~~~~~~~~~~~~`)
 | 
						|
 | 
						|
	loop(bufio.NewScanner(os.Stdin))
 | 
						|
}
 | 
						|
 | 
						|
const (
 | 
						|
	// skin options :-)
 | 
						|
	emptyMark = "☆"
 | 
						|
	mark1     = "💀"
 | 
						|
	mark2     = "🎈"
 | 
						|
 | 
						|
	maxTurn = 9
 | 
						|
)
 | 
						|
 | 
						|
type game struct {
 | 
						|
	board  [][]string
 | 
						|
	turn   int
 | 
						|
	player string
 | 
						|
 | 
						|
	buf strings.Builder
 | 
						|
}
 | 
						|
 | 
						|
func loop(in *bufio.Scanner) {
 | 
						|
	g := newGame()
 | 
						|
 | 
						|
	for {
 | 
						|
		g.prompt()
 | 
						|
 | 
						|
		if !in.Scan() {
 | 
						|
			break
 | 
						|
		}
 | 
						|
 | 
						|
		// Atoi already return 0 on error; no need to check
 | 
						|
		// it for the following switch to work
 | 
						|
		pos, _ := strconv.Atoi(in.Text())
 | 
						|
 | 
						|
		msg := g.play(pos)
 | 
						|
		if msg != "" {
 | 
						|
			fmt.Println("\n>>>", msg)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		if msg := g.finito(); msg != "" {
 | 
						|
			fmt.Printf("\n%s%s\n", g, msg)
 | 
						|
			break
 | 
						|
		}
 | 
						|
 | 
						|
		g.next()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func newGame() *game {
 | 
						|
	return &game{
 | 
						|
		player: mark1,
 | 
						|
		board: [][]string{
 | 
						|
			{emptyMark, emptyMark, emptyMark},
 | 
						|
			{emptyMark, emptyMark, emptyMark},
 | 
						|
			{emptyMark, emptyMark, emptyMark},
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (g *game) prompt() {
 | 
						|
	fmt.Printf("\n%s%s [1-9]: ", g, g.player)
 | 
						|
}
 | 
						|
 | 
						|
func (g *game) String() string {
 | 
						|
	g.buf.Reset()
 | 
						|
 | 
						|
	g.buf.WriteRune('\n')
 | 
						|
	g.buf.WriteString("---+----+---\n")
 | 
						|
 | 
						|
	for _, line := range g.board {
 | 
						|
		g.buf.WriteString(strings.Join(line, "  | "))
 | 
						|
		g.buf.WriteRune('\n')
 | 
						|
 | 
						|
		g.buf.WriteString("---+----+---\n")
 | 
						|
	}
 | 
						|
 | 
						|
	return g.buf.String()
 | 
						|
}
 | 
						|
 | 
						|
func (g *game) play(pos int) string {
 | 
						|
	var row int
 | 
						|
 | 
						|
	switch {
 | 
						|
	case pos >= 7 && pos <= 9:
 | 
						|
		row = 2
 | 
						|
	case pos >= 4 && pos <= 6:
 | 
						|
		row = 1
 | 
						|
	case pos >= 1 && pos <= 3:
 | 
						|
		row = 0
 | 
						|
	default:
 | 
						|
		return "wrong position"
 | 
						|
	}
 | 
						|
 | 
						|
	col := pos - row*3 - 1
 | 
						|
 | 
						|
	if g.board[row][col] != emptyMark {
 | 
						|
		return "already played!"
 | 
						|
	}
 | 
						|
 | 
						|
	// put the player
 | 
						|
	g.board[row][col] = g.player
 | 
						|
 | 
						|
	return ""
 | 
						|
}
 | 
						|
 | 
						|
func (g *game) finito() string {
 | 
						|
	g.turn++
 | 
						|
 | 
						|
	switch {
 | 
						|
	case g.won():
 | 
						|
		return fmt.Sprintf("WINNER: %s", g.player)
 | 
						|
	case g.turn == maxTurn:
 | 
						|
		return "TIE!"
 | 
						|
	}
 | 
						|
	return ""
 | 
						|
}
 | 
						|
 | 
						|
func (g *game) won() (won bool) {
 | 
						|
	for _, m := range [2]string{mark1, mark2} {
 | 
						|
		b, mmm := g.board, strings.Repeat(m, 3)
 | 
						|
 | 
						|
		won = /* horizontals */
 | 
						|
			strings.Join(b[0], "") == mmm ||
 | 
						|
				strings.Join(b[1], "") == mmm ||
 | 
						|
				strings.Join(b[2], "") == mmm ||
 | 
						|
 | 
						|
				/* verticals */
 | 
						|
				b[0][0]+b[1][0]+b[2][0] == mmm ||
 | 
						|
				b[0][1]+b[1][1]+b[2][1] == mmm ||
 | 
						|
				b[0][2]+b[1][2]+b[2][2] == mmm ||
 | 
						|
 | 
						|
				/* diagonals */
 | 
						|
				b[0][0]+b[1][1]+b[2][2] == mmm ||
 | 
						|
				b[0][2]+b[1][1]+b[2][0] == mmm
 | 
						|
 | 
						|
		if won {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
func (g *game) next() {
 | 
						|
	if g.player == mark1 {
 | 
						|
		g.player = mark2
 | 
						|
	} else {
 | 
						|
		g.player = mark1
 | 
						|
	}
 | 
						|
}
 |