func (eng *T3Engine) Exec(ctx context.Context, line string, ui io.ReadWriter) int {
	eng.board = make([][]Player, 3)
	for i := range eng.board {
		eng.board[i] = make([]Player, 3)
	}
	curPlayer := X
	for !eng.isOver() {
		eng.printBoard(ui)
		_, err := ui.Write(nil)
		if err != nil {
			log.Println(err)
			return 1
		}
		b := make([]byte, 2)
		_, err = ui.Read(b)
		if err == context.Canceled {
			return 1
		}
		if err != nil {
			log.Println(err)
			return 1
		}
		r, _ := utf8.DecodeRune(b)
		switch r {
		case '1':
			eng.board[2][0] = curPlayer
		case '2':
			eng.board[2][1] = curPlayer
		case '3':
			eng.board[2][2] = curPlayer
		case '4':
			eng.board[1][0] = curPlayer
		case '5':
			eng.board[1][1] = curPlayer
		case '6':
			eng.board[1][2] = curPlayer
		case '7':
			eng.board[0][0] = curPlayer
		case '8':
			eng.board[0][1] = curPlayer
		case '9':
			eng.board[0][2] = curPlayer
		default:
			select {
			case <-ctx.Done():
				return 0
			default:
			}
			fmt.Fprintln(ui, "Invalid position:", r)
		}
		if curPlayer == X {
			curPlayer = O
		} else {
			curPlayer = X
		}
	}
	eng.printBoard(ui)
	winner, ok := hasWinner(eng.board)
	if ok {
		fmt.Fprintf(ui, "Player %s won!\n", winner)
	} else {
		fmt.Fprintln(ui, "This game is a tie!")
	}
	return 0
}
Exec starts a Tic-Tac-Toe game
package main
import (
	"bytes"
	"context"
	"fmt"
	"github.com/Zaba505/sand"
	"io"
	"log"
	"os"
	"unicode/utf8"
)
type Player uint8
const (
	Nan Player = iota
	X
	O
)
func (p Player) String() string {
	switch p {
	case Nan:
		return " "
	case X:
		return "X"
	case O:
		return "O"
	}
	return "NoPlayer"
}
type T3Engine struct {
	board [][]Player
}