ref: 03c4b270665987e034d6589d595e4e6b638a02ab
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") }