add: new tictactoe game

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
This commit is contained in:
Inanc Gumus
2019-07-27 18:16:17 +03:00
parent e191567e1f
commit eb8f9987a8
97 changed files with 3457 additions and 3 deletions

View File

@@ -0,0 +1,149 @@
package main
import (
"fmt"
"math/rand"
"strings"
"time"
rainbow "github.com/guineveresaenger/golang-rainbow"
)
const (
maxTurn = 9
// skin options :-)
empty = " "
player1 = " X "
player2 = " O "
header = "---+---+---"
footer = "---+---+---"
separator = "|"
banner = `
~~~~~~~~~~~~~~~
TIC~TAC~TOE
~~~~~~~~~~~~~~~`
)
// -------------------------------------------------
// INITIALIZE THE GAME
// -------------------------------------------------
var (
turn int
won bool
board = [][]string{
{empty, empty, empty},
{empty, empty, empty},
{empty, empty, empty},
}
player = player1
)
func main() {
rand.Seed(time.Now().UnixNano())
rainbow.Rainbow(banner, strings.Count(banner, "\n"))
for {
// -------------------------------------------------
// PRINT THE BOARD AND THE PROMPT
// -------------------------------------------------
fmt.Printf("\n %s\n", header)
for _, line := range board {
fmt.Printf(" %s\n", strings.Join(line, separator))
fmt.Printf(" %s\n", footer)
}
// -------------------------------------------------
// IS THERE A WINNER? OR IS IT A TIE?
// -------------------------------------------------
for i := 1; i <= 2; i++ {
m := player2
if i == 1 {
m = player1
}
b, mmm := board, strings.Repeat(m, 3)
/* horizontals */
hor := strings.Join(b[0], "") == mmm ||
strings.Join(b[1], "") == mmm ||
strings.Join(b[2], "") == mmm
/* verticals */
ver := 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 */
diag := b[0][0]+b[1][1]+b[2][2] == mmm ||
b[0][2]+b[1][1]+b[2][0] == mmm
won = hor || ver || diag
if won {
player = m
break
}
}
if won {
player = strings.TrimSpace(player)
fmt.Printf("\n>>> WINNER: %s\n", player)
break
} else if turn == maxTurn {
fmt.Printf("\n>>> TIE!\n")
break
}
// -------------------------------------------------
// PLAY
// -------------------------------------------------
pos := rand.Intn(9) + 1
fmt.Printf("\nPlayer %s plays to %d\n", player, pos)
// -------------------------------------------------
// IS IT A VALID MOVE?
// -------------------------------------------------
var row int
switch {
case pos <= 3:
row = 0
case pos <= 6:
row = 1
case pos <= 9:
row = 2
default:
fmt.Println(">>>", "wrong position!")
continue
}
col := pos - row*3 - 1
if board[row][col] != empty {
fmt.Println(">>>", "already played!")
continue
}
// -------------------------------------------------
// MARK THE MOVE AND INCREMENT THE TURN
// -------------------------------------------------
// put a mark on the board
board[row][col] = player
// switch to the next player
if player == player1 {
player = player2
} else {
player = player1
}
turn++
// time.Sleep(time.Millisecond * 100)
}
}

View File

@@ -0,0 +1,150 @@
package main
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
rainbow "github.com/guineveresaenger/golang-rainbow"
)
const (
maxTurn = 9
// skin options :-)
empty = " "
player1 = " X "
player2 = " O "
header = "---+---+---"
footer = "---+---+---"
separator = "|"
banner = `
~~~~~~~~~~~~~~~
TIC~TAC~TOE
~~~~~~~~~~~~~~~`
)
// -------------------------------------------------
// INITIALIZE THE GAME
// -------------------------------------------------
var (
turn int
won bool
board = [3][3]string{
{empty, empty, empty},
{empty, empty, empty},
{empty, empty, empty},
}
player = player1
// for saving the replay.log
inputs []byte
)
func main() {
rainbow.Rainbow(banner, strings.Count(banner, "\n"))
in := bufio.NewScanner(os.Stdin)
for {
// -------------------------------------------------
// PRINT THE BOARD AND THE PROMPT
// -------------------------------------------------
fmt.Printf("\n %s\n", header)
for _, line := range board {
fmt.Printf(" %s\n", strings.Join(line[:], separator))
fmt.Printf(" %s\n", footer)
}
// -------------------------------------------------
// IS THERE A WINNER? OR IS IT A TIE?
// -------------------------------------------------
for _, m := range [2]string{player1, player2} {
b, mmm := 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 {
player = m
break
}
}
if won {
player = strings.TrimSpace(player)
fmt.Printf("\n>>> WINNER: %s\n", player)
break
} else if turn++; turn == maxTurn+1 {
fmt.Printf("\n>>> TIE!\n")
break
}
// -------------------------------------------------
// CHECK THE MOVE AND PLAY
// -------------------------------------------------
// get the input
fmt.Printf("\nPLAYER: %q [1-9]: ", player)
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())
// Save the input for replaying
inputs = append(inputs, byte(pos+48), '\n')
var row int
switch {
case pos >= 1 && pos <= 3:
row = 0
case pos >= 4 && pos <= 6:
row = 1
case pos >= 7 && pos <= 9:
row = 2
default:
fmt.Println("\n>>>", "wrong position!")
continue
}
col := pos - row*3 - 1
if board[row][col] != empty {
fmt.Println("\n>>>", "already played!")
continue
}
// put a mark on the board
board[row][col] = player
// switch to the next player
if player == player1 {
player = player2
} else {
player = player1
}
}
if err := ioutil.WriteFile("replay.log", inputs, 0644); err != nil {
fmt.Fprintf(os.Stderr, "Cannot save replay: ", err)
}
}

View File

@@ -0,0 +1,156 @@
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
const (
// skin options :-)
emptyMark = "☆"
mark1 = "💀"
mark2 = "🎈"
banner = `
~~~~~~~~~~~~~~~
TIC~TAC~TOE
~~~~~~~~~~~~~~~`
maxTurn = 9
)
func main() {
fmt.Println(banner)
loop(bufio.NewScanner(os.Stdin))
}
func loop(in *bufio.Scanner) {
var (
turn int
player = mark1
board = createBoard()
)
for {
prompt(board, player)
if !in.Scan() {
break
}
msg := play(board, player, getMove(in.Text()))
if msg != "" {
fmt.Println("\n>>>", msg)
continue
}
turn++
if msg := finito(board, turn, player); msg != "" {
printBoard(board, player)
fmt.Printf("\n%s\n", msg)
break
}
player = switchTo(player)
}
}
func createBoard() [][]string {
return [][]string{
{emptyMark, emptyMark, emptyMark},
{emptyMark, emptyMark, emptyMark},
{emptyMark, emptyMark, emptyMark},
}
}
func prompt(board [][]string, player string) {
printBoard(board, player)
fmt.Printf("\n%s [1-9]: ", player)
}
func printBoard(board [][]string, player string) {
fmt.Println()
fmt.Println("---+----+---")
for _, line := range board {
fmt.Printf("%s\n", strings.Join(line, " | "))
fmt.Println("---+----+---")
}
}
func getMove(move string) int {
// Atoi already return 0 on error; no need to check
// it for the following switch to work
pos, _ := strconv.Atoi(move)
return pos
}
func play(board [][]string, player string, 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 board[row][col] != emptyMark {
return "already played!"
}
// put the player
board[row][col] = player
return ""
}
func finito(board [][]string, turn int, player string) string {
switch {
case won(board):
return fmt.Sprintf("WINNER: %s", player)
case turn == maxTurn:
return "TIE!"
}
return ""
}
func won(board [][]string) (won bool) {
for _, m := range [2]string{mark1, mark2} {
b, mmm := 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 switchTo(player string) string {
if player == mark1 {
return mark2
}
return mark1
}

View File

@@ -0,0 +1,162 @@
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
const (
// skin options :-)
emptyMark = "☆"
mark1 = "💀"
mark2 = "🎈"
banner = `
~~~~~~~~~~~~~~~
TIC~TAC~TOE
~~~~~~~~~~~~~~~`
maxTurn = 9
)
type game struct {
board [][]string
turn int
player string
}
func main() {
fmt.Println(banner)
loop(bufio.NewScanner(os.Stdin))
}
func loop(in *bufio.Scanner) {
g := game{
player: mark1,
board: createBoard(),
}
for {
prompt(g)
if !in.Scan() {
break
}
msg := play(g, getMove(in.Text()))
if msg != "" {
fmt.Println("\n>>>", msg)
continue
}
g.turn++
if msg := finito(g); msg != "" {
printBoard(g.board)
fmt.Printf("\n%s\n", msg)
break
}
g.player = switchTo(g.player)
}
}
func createBoard() [][]string {
return [][]string{
{emptyMark, emptyMark, emptyMark},
{emptyMark, emptyMark, emptyMark},
{emptyMark, emptyMark, emptyMark},
}
}
func prompt(g game) {
printBoard(g.board)
fmt.Printf("\n%s [1-9]: ", g.player)
}
func printBoard(board [][]string) {
fmt.Println()
fmt.Println("---+----+---")
for _, line := range board {
fmt.Printf("%s\n", strings.Join(line, " | "))
fmt.Println("---+----+---")
}
}
func getMove(move string) int {
// Atoi already return 0 on error; no need to check
// it for the following switch to work
pos, _ := strconv.Atoi(move)
return pos
}
// NOTE: manipulates the game object
func play(g game, 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 finito(g game) string {
switch {
case won(g.board):
return fmt.Sprintf("WINNER: %s", g.player)
case g.turn == maxTurn:
return "TIE!"
}
return ""
}
func won(board [][]string) (won bool) {
for _, m := range [2]string{mark1, mark2} {
b, mmm := 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 switchTo(player string) string {
if player == mark1 {
return mark2
}
return mark1
}

View File

@@ -0,0 +1,158 @@
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
const (
// skin options :-)
emptyMark = "☆"
mark1 = "💀"
mark2 = "🎈"
banner = `
~~~~~~~~~~~~~~~
TIC~TAC~TOE
~~~~~~~~~~~~~~~`
maxTurn = 9
)
func main() {
fmt.Println(banner)
loop(bufio.NewScanner(os.Stdin))
}
type game struct {
board [][]string
turn int
player string
}
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
}
g.turn++
if msg := g.finito(); msg != "" {
g.print()
fmt.Printf("\n%s\n", msg)
break
}
g.player = switchTo(g.player)
}
}
func newGame() game {
return game{
player: mark1,
board: [][]string{
{emptyMark, emptyMark, emptyMark},
{emptyMark, emptyMark, emptyMark},
{emptyMark, emptyMark, emptyMark},
},
}
}
func (g game) prompt() {
g.print()
fmt.Printf("\n%s [1-9]: ", g.player)
}
func (g game) print() {
fmt.Println()
fmt.Println("---+----+---")
for _, line := range g.board {
fmt.Printf("%s\n", strings.Join(line, " | "))
fmt.Println("---+----+---")
}
}
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 {
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 switchTo(player string) string {
if player == mark1 {
return mark2
}
return mark1
}

View File

@@ -0,0 +1,166 @@
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
}
}

View File

@@ -0,0 +1,152 @@
package main
import (
"strings"
)
type state int
const (
maxTurn = 9
wrongPosition = -2
statePlaying state = iota
stateWon
stateTie
stateAlreadyPlayed
stateWrongPosition
)
type game struct {
board [][]string
turn int
player string
skin // embed the skin
logger // embed the logger
}
func newGame(s skin, l logger) *game {
return &game{
player: s.mark1,
board: [][]string{
{s.empty, s.empty, s.empty},
{s.empty, s.empty, s.empty},
{s.empty, s.empty, s.empty},
},
skin: s,
logger: l,
}
}
func (g *game) play(pos int) state {
if st := g.move(pos); st != statePlaying {
return st
}
g.turn++ // increment the turn
// first check the winner then check the tie
// or the last mover won't win
switch {
case g.won():
return stateWon
case g.turn == maxTurn:
return stateTie
}
g.changePlayer()
return statePlaying
}
func (g *game) move(pos int) state {
row, col := position(pos)
if row+col == wrongPosition {
return stateWrongPosition
}
if g.board[row][col] != g.empty {
return stateAlreadyPlayed
}
// put the player's mark on the board
g.board[row][col] = g.player
return statePlaying
}
// we can detect the winning state just by comparing the strings
// because, the game board is a bunch of strings
func (g *game) won() (won bool) {
for _, m := range [2]string{g.mark1, g.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
}
// this method should have a pointer receiver
// because, it changes the game value
func (g *game) changePlayer() {
if g.player == g.mark1 {
g.player = g.mark2
} else {
g.player = g.mark1
}
}
func (g *game) print() {
g.Println()
g.Println(g.header)
for i, line := range g.board {
g.Print(g.separator)
for _, m := range line {
g.Printf("%2s%s", m, g.separator)
}
if i+1 != len(g.board) {
g.Printf("\n%s\n", g.middle)
}
}
g.Printf("\n%s\n", g.footer)
}
// this function doesn't depend on the game state
// so, make it a function instead of a method
func position(pos int) (row, col int) {
switch {
case pos >= 1 && pos <= 3:
row = 0
case pos >= 4 && pos <= 6:
row = 1
case pos >= 7 && pos <= 9:
row = 2
default:
return -1, -1
}
return row, pos - row*3 - 1
}

View File

@@ -0,0 +1,19 @@
package main
type logger struct {
print func(...interface{}) (int, error)
printf func(fmt string, args ...interface{}) (int, error)
println func(...interface{}) (int, error)
}
func (l logger) Print(args ...interface{}) {
l.print(args...)
}
func (l logger) Printf(fmt string, args ...interface{}) {
l.printf(fmt, args...)
}
func (l logger) Println(args ...interface{}) {
l.println(args...)
}

View File

@@ -0,0 +1,93 @@
package main
import (
"bufio"
"fmt"
"os"
"strconv"
c "github.com/fatih/color"
rainbow "github.com/guineveresaenger/golang-rainbow"
)
// TODO: move the main logic into a helper
func main() {
const banner = `
~~~~~~~~~~~~~~~~
TIC~TAC~TOE
~~~~~~~~~~~~~~~~`
in := bufio.NewScanner(os.Stdin)
sk := selectSkin(in)
lg := logger{
print: fmt.Print,
printf: fmt.Printf,
println: fmt.Println,
}
for {
g := newGame(sk, lg)
game:
for {
rainbow.Rainbow(banner, 3)
g.print()
fmt.Printf(c.CyanString("\n%s [1-9]: ", g.player))
if !in.Scan() {
break
}
pos, _ := strconv.Atoi(in.Text())
switch st := g.play(pos); st {
case stateAlreadyPlayed, stateWrongPosition:
announce(g, st)
continue
case stateWon, stateTie:
announce(g, st)
break game
}
g.Print("\033[2J")
}
fmt.Print(c.MagentaString("One more game? [y/n]: "))
if in.Scan(); in.Text() != "y" {
fmt.Println("OK, bye!")
break
}
}
}
func announce(g *game, st state) {
red := c.New(c.FgRed, c.Bold)
green := c.New(c.BgBlack, c.FgGreen, c.Bold)
switch st {
case stateAlreadyPlayed, stateWrongPosition:
red.Printf("\n>>> You can't play there!\n")
case stateWon:
g.print()
green.Printf("\nWINNER: %s\n", g.player)
case stateTie:
g.print()
green.Printf("\nTIE!\n")
}
}
func selectSkin(in *bufio.Scanner) skin {
fmt.Println(c.MagentaString("Our finest selection of skins:"))
for name := range skins {
fmt.Printf("- %s\n", name)
}
fmt.Print(c.GreenString("\nEnter the name of the skin: "))
in.Scan()
if sk, ok := skins[in.Text()]; ok {
return sk
}
return defaultSkin
}

View File

@@ -0,0 +1,5 @@
https://github.com/mattn/go-runewidth
https://github.com/olekukonko/tablewriter
https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c

View File

@@ -0,0 +1,91 @@
package main
import "github.com/fatih/color"
// skin options :-)
type skin struct {
empty, mark1, mark2 string
header, middle, footer, separator string
unicode bool
}
var skins = map[string]skin{
"colorful": colorfulSkin,
"poseidon": poseidonSkin,
"statues": statuesSkin,
"aliens": aliensSkin,
"snow": snowSkin,
"monkeys": monkeysSkin,
}
var defaultSkin = skin{
empty: " ",
mark1: " X ",
mark2: " O ",
header: "┌───┬───┬───┐",
middle: "├───┼───┼───┤",
footer: "└───┴───┴───┘",
separator: "│",
}
var colorfulSkin = skin{
empty: " ",
mark1: color.CyanString(" X "),
mark2: color.HiMagentaString(" O "),
header: color.HiBlueString("┌───┬───┬───┐"),
middle: color.HiBlueString("├───┼───┼───┤"),
footer: color.HiBlueString("└───┴───┴───┘"),
separator: color.BlueString("│"),
}
var poseidonSkin = skin{
empty: "❓ ",
mark1: "🔱 ",
mark2: "⚓️ ",
header: "●————●————●————●",
middle: "●————●————●————●",
footer: "●————●————●————●",
separator: "⎮ ",
}
var statuesSkin = skin{
empty: "❓ ",
mark1: "🗿 ",
mark2: "🗽 ",
header: "┌────┬────┬────┐",
middle: "├────┼────┼────┤",
footer: "└────┴────┴────┘",
separator: "│ ",
}
var aliensSkin = skin{
empty: "❓ ",
mark1: "👽 ",
mark2: "👾 ",
header: "┌────┬────┬────┐",
middle: "├────┼────┼────┤",
footer: "└────┴────┴────┘",
separator: "│ ",
}
var snowSkin = skin{
empty: "❓ ",
mark1: "⛄ ",
mark2: "❄️ ",
header: "╔════╦════╦════╗",
middle: "╠════╬════╬════╣",
footer: "╚════╩════╩════╝",
separator: "║ ",
}
var monkeysSkin = skin{
empty: "🍌 ",
mark1: "🙈 ",
mark2: "🙉 ",
header: "┌────┬────┬────┐",
middle: "├────┼────┼────┤",
footer: "└────┴────┴────┘",
separator: "│ ",
}

View File

@@ -0,0 +1,3 @@
**Check out:**
Tic-Tac-Toe Gist:
https://gist.github.com/agalal/bff3cb779c337274d2a3462c630f4d74