ref: e82dcee1a3d54f14c93a19f8fe21965ea72c56ce
dir: /files.go/
package main
import (
"os"
"io"
"time"
"fmt"
"strings"
"strconv"
)
// Generic file interface
// Chess root directory
type ChessRootDir struct {
board *Board
files chan os.FileInfo
uid string
gid string
}
func NewChessRootDir(board *Board, uid, gid string) *ChessRootDir {
files := make(chan os.FileInfo, 8)
allFiles := []os.FileInfo {
&ChessControlFile {
board: board,
uid: uid,
gid: gid,
},
&BoardFile {
board: board,
uid: uid,
gid: gid,
},
&ChessStatusFile {
board: board,
uid: uid,
gid: gid,
},
&ChessNotifyFile {
board: board,
uid: uid,
gid: gid,
},
&GameHistory {
board: board,
uid: uid,
gid: gid,
},
}
for _, file := range allFiles {
files <- file
}
return &ChessRootDir {
board: board,
files: files,
}
}
func (d ChessRootDir) Name() string {
return "/"
}
func (d ChessRootDir) Size() int64 {
return 0
}
func (d ChessRootDir) Mode() os.FileMode {
return 0755
}
func (d ChessRootDir) ModTime() time.Time {
return time.Unix(0, 0)
}
func (d ChessRootDir) IsDir() bool {
return true
}
func (d ChessRootDir) Sys() interface{} {
return nil
}
func (d ChessRootDir) Uid() string {
return d.uid
}
func (d ChessRootDir) Gid() string {
return d.gid
}
func (d ChessRootDir) Muid() string {
return d.uid
}
func (d ChessRootDir) Readdir(n int) ([]os.FileInfo, error) {
var err error
files := make([]os.FileInfo, 0, 8)
ReadLoop:
for i := 0; i < n; i++ {
select {
case file := <-d.files:
files = append(files, file)
default:
err = io.EOF
break ReadLoop
}
}
return files, err
}
func (d ChessRootDir) Close() error {
return nil
}
// Control file
type ChessControlFile struct {
board *Board
uid string
gid string
}
func (f ChessControlFile) Name() string {
return "ctl"
}
func (f ChessControlFile) Size() int64 {
return 0
}
func (f ChessControlFile) Mode() os.FileMode {
return 0664
}
func (f ChessControlFile) ModTime() time.Time {
return time.Unix(0, 0)
}
func (f ChessControlFile) IsDir() bool {
return false
}
func (f ChessControlFile) Sys() interface{} {
return nil
}
func (f ChessControlFile) Close() error {
return nil
}
func (f ChessControlFile) Uid() string {
return f.uid
}
func (f ChessControlFile) Gid() string {
return f.gid
}
func (f ChessControlFile) Muid() string {
return f.uid
}
func (f ChessControlFile) ReadAt(p []byte, off int64) (int, error) {
return 0, fmt.Errorf("not supported")
}
func (f ChessControlFile) WriteAt(p []byte, off int64) (int, error) {
if off > 0 {
return 0, fmt.Errorf("not supported")
}
msgRaw := strings.Split(strings.Trim(string(p), " \n\r\t"), " ")
var msg []string
for _, part := range msgRaw {
if part != "" {
msg = append(msg, part)
}
}
if len(msg) == 0 {
return 0, fmt.Errorf("unknown command; supported: new, move, load")
}
switch (msg[0]) {
case "new":
if len(msg) != 1 {
return 0, fmt.Errorf("wrong arguments; usage: new")
}
f.board.Reset()
case "move":
if len(msg) != 2 {
return 0, fmt.Errorf("wrong arguments: usage: move X")
}
moveRaw := msg[1]
move, err := NewMove(moveRaw, f.board)
if err != nil {
return 0, err
}
err = f.board.MovePiece(move)
if err != nil {
return 0, err
}
case "load":
pgn := msg[1:]
var moves []*Move
var currentMove = 0
var relPos = 0
PgnRead:
for i := 0; i < len(pgn); i++ {
if relPos == 0 {
if pgn[i][len(pgn[i]) - 1] != '.' {
return 0, fmt.Errorf("unexpected character in element: %s", pgn[i])
}
newMoveStr := pgn[i][0:(len(pgn[i]) - 1)]
newMove, err := strconv.Atoi(newMoveStr)
if err != nil {
return 0, err
}
if currentMove + 1 != newMove {
return 0, fmt.Errorf("incorrect move order")
}
currentMove = currentMove + 1
relPos = relPos + 1
continue PgnRead
}
moveStr := pgn[i]
move, err := NewMove(moveStr, f.board)
if err != nil {
return 0, err
}
moves = append(moves, move)
relPos = (relPos + 1) % 3
}
f.board.Reset()
for i := 0; i < len(moves); i++ {
f.board.MovePiece(moves[i])
}
default:
return 0, fmt.Errorf("unknown command; supported: new, move, load")
}
return len(p), nil
}
// Board file
type BoardFile struct {
board *Board
uid string
gid string
}
func (f BoardFile) Name() string {
return "board"
}
func (f BoardFile) Size() int64 {
return 0
}
func (f BoardFile) Mode() os.FileMode {
return 0644
}
func (f BoardFile) ModTime() time.Time {
return time.Unix(0, 0)
}
func (f BoardFile) IsDir() bool {
return false
}
func (f BoardFile) Sys() interface{} {
return nil
}
func (f BoardFile) Close() error {
return nil
}
func (f BoardFile) Uid() string {
return f.uid
}
func (f BoardFile) Gid() string {
return f.gid
}
func (f BoardFile) Muid() string {
return f.uid
}
func (f BoardFile) ReadAt(p []byte, off int64) (int, error) {
var boardLines [19]string
// board drawing
for i := 0; i < 19; i++ {
switch (i % 2) {
case 0:
for j := 0; j < 19; j++ {
switch(j % 2) {
case 0:
boardLines[i] = fmt.Sprintf("%s ", boardLines[i])
case 1:
boardLines[i] = fmt.Sprintf("%s|", boardLines[i])
}
}
case 1:
for j := 0; j < 19; j++ {
boardLines[i] = fmt.Sprintf("%s-", boardLines[i])
}
}
}
// board marks
for i := 0; i < 8; i++ {
line := []rune(boardLines[(i + 1) * 2])
line[0] = rune(49 + (7 - i))
boardLines[(i + 1) * 2] = string(line)
}
for i := 0; i < 8; i++ {
line := []rune(boardLines[18])
line[(i + 1) * 2] = rune(97 + i)
boardLines[18] = string(line)
}
// board pieces
for i := 0; i < 8; i++ {
for j := 0; j < 8; j++ {
piece := f.board.Board[i][j]
if piece != nil {
line := []rune(boardLines[(i + 1) * 2])
line[(j + 1) * 2] = piece.ToPretty()
boardLines[(i + 1) * 2] = string(line)
}
}
}
// final board output
var content string
for i := 0; i < 19; i++ {
content = fmt.Sprintf("%s\n%s", content, boardLines[i])
}
content = fmt.Sprintf("%s\n", content)
if off > int64(len(content)) {
return 0, io.EOF
}
readContent := content[off:]
n := copy(p, readContent)
return n, nil
}
func (f BoardFile) WriteAt(p []byte, off int64) (int, error) {
return 0, fmt.Errorf("not supported")
}
// Status file
type ChessStatusFile struct {
board *Board
uid string
gid string
}
func (f ChessStatusFile) Name() string {
return "status"
}
func (f ChessStatusFile) Size() int64 {
return 0
}
func (f ChessStatusFile) Mode() os.FileMode {
return 0644
}
func (f ChessStatusFile) ModTime() time.Time {
return time.Unix(0, 0)
}
func (f ChessStatusFile) IsDir() bool {
return false
}
func (f ChessStatusFile) Sys() interface{} {
return nil
}
func (f ChessStatusFile) Close() error {
return nil
}
func (f ChessStatusFile) Uid() string {
return f.uid
}
func (f ChessStatusFile) Gid() string {
return f.gid
}
func (f ChessStatusFile) Muid() string {
return f.uid
}
func (f ChessStatusFile) ReadAt(p []byte, off int64) (int, error) {
f.board.UpdateTime()
secondsWhite := f.board.SecondsWhite
secondsBlack := f.board.SecondsBlack
playerTime := f.board.PlayerTime
var turn string
switch (f.board.Turn) {
case White:
turn = "White"
case Black:
turn = "Black"
}
var status string
status = fmt.Sprintf("White: %d\n", secondsWhite)
status = fmt.Sprintf("%sBlack: %d\n", status, secondsBlack)
status = fmt.Sprintf("%sTurn: %s\n", status, turn)
status = fmt.Sprintf("%sPlayer time: %d\n", status, playerTime)
if off > int64(len(status)) {
return 0, io.EOF
}
content := status[off:]
n := copy(p, content)
return n, nil
}
func (f ChessStatusFile) WriteAt(p []byte, off int64) (int, error) {
return 0, fmt.Errorf("not supported")
}
// Notify file
type ChessNotifyFile struct {
board *Board
uid string
gid string
}
func (f ChessNotifyFile) Name() string {
return "notify"
}
func (f ChessNotifyFile) Size() int64 {
return 0
}
func (f ChessNotifyFile) Mode() os.FileMode {
return 0644
}
func (f ChessNotifyFile) ModTime() time.Time {
return time.Unix(0, 0)
}
func (f ChessNotifyFile) IsDir() bool {
return false
}
func (f ChessNotifyFile) Sys() interface{} {
return nil
}
func (f ChessNotifyFile) Close() error {
return nil
}
func (f ChessNotifyFile) Uid() string {
return f.uid
}
func (f ChessNotifyFile) Gid() string {
return f.gid
}
func (f ChessNotifyFile) Muid() string {
return f.uid
}
func (f ChessNotifyFile) ReadAt(p []byte, off int64) (int, error) {
turn := f.board.Turn
if off > 0 {
return 0, io.EOF
}
for turn == f.board.Turn {
d, _ := time.ParseDuration("100ms")
time.Sleep(d)
}
var turnRep string
switch (f.board.Turn) {
case White:
turnRep = "white\n"
case Black:
turnRep = "black\n"
}
n := copy(p, turnRep)
return n, nil
}
func (f ChessNotifyFile) WriteAt(p []byte, off int64) (int, error) {
return 0, fmt.Errorf("not supported")
}
// History file
type GameHistory struct {
board *Board
uid string
gid string
}
func (f GameHistory) Name() string {
return "history"
}
func (f GameHistory) Size() int64 {
return 0
}
func (f GameHistory) Mode() os.FileMode {
return 0644
}
func (f GameHistory) ModTime() time.Time {
return time.Unix(0, 0)
}
func (f GameHistory) IsDir() bool {
return false
}
func (f GameHistory) Sys() interface{} {
return nil
}
func (f GameHistory) Close() error {
return nil
}
func (f GameHistory) Uid() string {
return f.uid
}
func (f GameHistory) Gid() string {
return f.gid
}
func (f GameHistory) Muid() string {
return f.uid
}
func (f GameHistory) ReadAt(p []byte, off int64) (int, error) {
var historyContent = ""
for i := 0; i < len(f.board.History); i = i + 2 {
historyContent = fmt.Sprintf("%s %d. %s",
historyContent,
(i / 2) + 1,
f.board.History[i])
if i + 1 < len(f.board.History) {
historyContent = fmt.Sprintf("%s %s",
historyContent,
f.board.History[i + 1])
}
}
if off > 0 {
return 0, io.EOF
}
historyContent = fmt.Sprintf("%s\n", historyContent)
n := copy(p, historyContent)
return n, nil
}
func (f GameHistory) WriteAt(p []byte, off int64) (int, error) {
return 0, fmt.Errorf("not supported")
}