godgt

package module
v0.0.0-...-e857748 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 12, 2018 License: GPL-3.0 Imports: 21 Imported by: 0

README

godgt

Deprecated

This project has now been deprecated in favour of dogmatic, which does a much better job all round. This project will remain here, as it also contains useful utilities that will probably eventually be split out into separate projects.

About

An experimental utility for interacting with DGT's Electronic Chess Boards, written in Go. The development platform is Linux; on other platforms, YMMV.

There are two executables, one (rawdump) for dumping out more or less raw position updates from the board, and another (dgtd) for attempting to turn these into actual moves.

The latter task is not as straightforward as it might seem. The reason for this is that the board does not "understand" chess; it just reports low-level piece move events.

For example, the move "exd5" might be received as three events, as follows:

  1. EMPTY to e4 (White picks up his pawn on e4)
  2. EMPTY to d5 (White removes the captured black pawn on d5)
  3. WHITE PAWN to d5 (White places his pawn on d5)

Worse still, because of the sequential nature of the scanning of the board, these might be received out of order. Out of order events can also occur due to player behaviour; for example, removing a captured piece before moving the capturing piece.

However, a long slow slide of a piece along a rank, file or diagonal can produce a "storm" of updates as several squares along the path detect the temporary presence of the piece over the square.

Even worse is the situation when a player drops one or several pieces, or makes and retracts an invalid move.

The code to turn the events into moves is quite sophisticated, but it's not particularly robust; it can easily get itself into a situation whereby it considers any additional input from the board to be invalid.

For this reason, if you want to simply log moves from the board your best best is currently to run the rawdump utility and reconstruct the moves manually after the fact.

Installation

go get github.com/kgigitdev/godgt

Dependencies

go get github.com/jacobsa/go-serial/serial
go get github.com/jessevdk/go-flags
go get github.com/malbrecht/chess

Information

Luckily, the DGT boards don't need any additional driver under Linux: plugging one in should automatically load the FTDI USB-serial driver (ftdi_sio and usbserial) on a reasonably up-to-date Linux installation. You should also see a serial device like the following appear:

$ ls -la /dev/ttyUSB*
crw-rw---- 1 root dialout 188, 0 Apr 11 18:52 /dev/ttyUSB0

In order to make this readable by you, you might need to add yourself to the group owning the device, using a command like the following, after which you will need to log out and back in.

sudo usermod -a -G dialout ${USER}

Compiling and running

cd rawdump
go build
./rawdump --pngs

Output

Log output looks like:

2016/10/11 22:39:05 BOARD: r1bqkbnr/pp1p1ppp/2p5/n3p3/P3P3/2PP1N2/RP3PPP/1NBQKB1R w K a1 0 0
2016/10/11 22:39:05 r bqkbnr
2016/10/11 22:39:05 pp p ppp
2016/10/11 22:39:05   p     
2016/10/11 22:39:05 n   p   
2016/10/11 22:39:05 P   P   
2016/10/11 22:39:05   PP N  
2016/10/11 22:39:05 RP   PPP
2016/10/11 22:39:05  NBQKB R

The --pngs option will also cause one .png file of the position to be created for each field update:

$ ls *.png
boardupdate-0001.png  boardupdate-0008.png  boardupdate-0015.png
boardupdate-0002.png  boardupdate-0010.png  boardupdate-0016.png
boardupdate-0004.png  boardupdate-0012.png
boardupdate-0006.png  boardupdate-0014.png

Note that there will be many more .png files than there are moves in the game. However, it's much easier to reconstruct the game from these .png files than from the FEN strings.

Embedded Assets

The assets/ directory contains .png image files for the PNG writer, as well as .svg files.

These were created as described in the file assets/images/README

These are not used directly at runtime; instead, they are bundled into a static filesystem, in assets.go, using the excellent enc utility:

go install github.com/mjibson/esc
esc -o assets.go -pkg godgt assets

You should not need this utility unless you want to recreate assets.go.

History

This is something like my fourth attempt to write this utility. The first couple got bogged down by trying to write a general-purpose chess library in Go, in the spirit of python-chess.

The third attempt gave up on that and used malbrecht/chess instead, but also got bogged down attempting to be a general-purpose bridge between the DGT board, xboard, FICS, and stockfish, as well as hosting an HTTP server for viewing the game in progress.

This new, much less ambitious, attempt is cobbled together from pieces of the previous failed attempts. Its code style is therefore rather haphazard. Notably, most of the older salvaged code was written in a "everything is a pointer to a struct" object-oriented-ish style; later code is consciously experimenting with not doing that.

Documentation

Index

Constants

View Source
const BBISHOP = 0x0a
View Source
const BCROWN = 0x0a
View Source
const BDISK = 0x04
View Source
const BKING = 0x0b
View Source
const BKNIGHT = 0x09
View Source
const BPAWN = 0x07
View Source
const BQUEEN = 0x0c
View Source
const BROOK = 0x08
View Source
const DGT_BATTERY_STATUS = 0x20

Added for Bluetooth board

View Source
const DGT_BOARD_DUMP = 0x06
View Source
const DGT_BOARD_DUMP_50B = 0x14

Added for Draughts board

View Source
const DGT_BOARD_DUMP_50W = 0x15
View Source
const DGT_BUSADRES = 0x10
View Source
const DGT_BWTIME = 0x0d
View Source
const DGT_CLOCK_MESSAGE = 0x2b
View Source
const DGT_CMD_CLOCK_ASCII = 0x0c
View Source
const DGT_CMD_CLOCK_BEEP = 0x0b
View Source
const DGT_CMD_CLOCK_BUTTON = 0x08
View Source
const DGT_CMD_CLOCK_DISPLAY = 0x01
View Source
const DGT_CMD_CLOCK_END = 0x03
View Source
const DGT_CMD_CLOCK_ICONS = 0x02
View Source
const DGT_CMD_CLOCK_SETNRUN = 0x0a
View Source
const DGT_CMD_CLOCK_VERSION = 0x09
View Source
const DGT_EE_MOVES = 0x0f
View Source
const DGT_FIELD_UPDATE = 0x0e
View Source
const DGT_LONG_SERIALNR = 0x22
View Source
const DGT_MSG_BATTERY_STATUS = (MESSAGE_BIT | DGT_BATTERY_STATUS)

------------------------------------------------------------------------ Retrieve the battery status from the Bluetooth board

View Source
const DGT_MSG_BOARD_DUMP = (MESSAGE_BIT | DGT_BOARD_DUMP)

------------------------------------------------------------------------ DGT_MSG_BOARD_DUMP is the message that follows on a DGT_SEND_BOARD * command

View Source
const DGT_MSG_BOARD_DUMP_50B = (MESSAGE_BIT | DGT_BOARD_DUMP_50B)

------------------------------------------------------------------------ message format DGT_MSG_BOARD_DUMP_50B

View Source
const DGT_MSG_BOARD_DUMP_50W = (MESSAGE_BIT | DGT_BOARD_DUMP_50W)

------------------------------------------------------------------------ message format DGT_MSG_BOARD_DUMP_50W

View Source
const DGT_MSG_BUSADRES = (MESSAGE_BIT | DGT_BUSADRES)

------------------------------------------------------------------------ Message format DGT_MSG_BUSADRES return message with bus adres

View Source
const DGT_MSG_BWTIME = (MESSAGE_BIT | DGT_BWTIME)

------------------------------------------------------------------------ message format DGT_MSG_BWTIME

View Source
const DGT_MSG_EE_MOVES = (MESSAGE_BIT | DGT_EE_MOVES)

------------------------------------------------------------------------ DGT_SIZE_EE_MOVES is defined in dgt_ee1.h: current (0x2000-0x100+3)

View Source
const DGT_MSG_FIELD_UPDATE = (MESSAGE_BIT | DGT_FIELD_UPDATE)

------------------------------------------------------------------------ message format DGT_MSG_FIELD_UPDATE:

View Source
const DGT_MSG_LONG_SERIALNR = (MESSAGE_BIT | DGT_LONG_SERIALNR)

------------------------------------------------------------------------ Message format DGT_LONG_SERIALNR

View Source
const DGT_MSG_SERIALNR = (MESSAGE_BIT | DGT_SERIALNR)

------------------------------------------------------------------------ Message format DGT_MSG_SERIALNR return message with bus adres

View Source
const DGT_MSG_TRADEMARK = (MESSAGE_BIT | DGT_TRADEMARK)

------------------------------------------------------------------------ message format: DGT_MSG_TRADEMARK which returns a trade mark message

View Source
const DGT_MSG_VERSION = (MESSAGE_BIT | DGT_VERSION)

------------------------------------------------------------------------ Message format DGT_MSG_VERSION return message with bus adres

View Source
const DGT_NONE = 0x00

ID codes:

View Source
const DGT_RETURN_BUSADRES = 0x46
View Source
const DGT_RETURN_LONG_SERIALNR = 0x55
View Source
const DGT_RETURN_SERIALNR = 0x45
View Source
const DGT_SCAN_100 = 0x54
View Source
const DGT_SCAN_50B = 0x51
View Source
const DGT_SCAN_50W = 0x53
View Source
const DGT_SEND_BATTERY_STATUS = 0x4c
View Source
const DGT_SEND_BRD = 0x42
View Source
const DGT_SEND_BRD_50B = 0x50
View Source
const DGT_SEND_BRD_50W = 0x52
View Source
const DGT_SEND_CLK = 0x41
View Source
const DGT_SEND_EE_MOVES = 0x49
View Source
const DGT_SEND_RESET = 0x40
View Source
const DGT_SEND_TRADEMARK = 0x47
View Source
const DGT_SEND_UPDATE = 0x43
View Source
const DGT_SEND_UPDATE_BRD = 0x44
View Source
const DGT_SEND_UPDATE_NICE = 0x4b
View Source
const DGT_SEND_VERSION = 0x4d
View Source
const DGT_SERIALNR = 0x11
View Source
const DGT_SET_LEDS = 0x60
View Source
const DGT_SIZE_BATTERY_STATUS = 7
View Source
const DGT_SIZE_BOARD_DUMP = 67
View Source
const DGT_SIZE_BOARD_DUMP_50B = 53
View Source
const DGT_SIZE_BOARD_DUMP_50W = 53
View Source
const DGT_SIZE_BOARD_DUMP_DRAUGHTS = 103
View Source
const DGT_SIZE_BUSADRES = 5
View Source
const DGT_SIZE_BWTIME = 10
View Source
const DGT_SIZE_FIELD_UPDATE = 5
View Source
const DGT_SIZE_LONG_SERIALNR = 13
View Source
const DGT_SIZE_SERIALNR = 8
View Source
const DGT_SIZE_VERSION = 5
View Source
const DGT_STARTBOOTLOADER = 0x4e
View Source
const DGT_TO_BUSMODE = 0x4a
View Source
const DGT_TRADEMARK = 0x12
View Source
const DGT_VERSION = 0x13
View Source
const EMPTY = 0x00

Piece codes for chess pieces:

View Source
const MESSAGE_BIT = 0x80

the Message ID is the logical OR of MESSAGE_BIT and ID code

View Source
const MESSAGE_MASK = 0x7F
View Source
const PIECE1 = 0x0d /* Magic piece: Draw */
View Source
const PIECE2 = 0x0e /* Magic piece: White win */
View Source
const PIECE3 = 0x0f /* Magic piece: Black win */
View Source
const WBISHOP = 0x04
View Source
const WCROWN = 0x07
View Source
const WDISK = 0x01

For the draughts board

View Source
const WKING = 0x05
View Source
const WKNIGHT = 0x03
View Source
const WPAWN = 0x01
View Source
const WQUEEN = 0x06
View Source
const WROOK = 0x02

Variables

View Source
var ERR_CLOCK_ACK = errors.New("Clock ACK (Unhandled)")
View Source
var ERR_CLOCK_NOT_CONNECTED = errors.New("Clock Not Connected")
View Source
var ERR_CLOCK_NOT_RUNNNING = errors.New("Clock Not Running")
View Source
var ERR_NONE_COMMAND = errors.New("NONE Command")
View Source
var ERR_NOT_ENOUGH_DATA = errors.New("Not enough data")
View Source
var ERR_PARSE_FAILED = errors.New("Failed to parse bytes")
View Source
var ERR_UNHANDLED = errors.New("Unhandled")

Functions

func CopyFile

func CopyFile(src string, dst string) error

CopyFile makes a copy of a file

func CreatePort

func CreatePort(portName string) (io.ReadWriteCloser, error)

func Dir

func Dir(useLocal bool, name string) http.FileSystem

Dir returns a http.Filesystem for the embedded assets on a given prefix dir. If useLocal is true, the filesystem's contents are instead used.

func FS

func FS(useLocal bool) http.FileSystem

FS returns a http.Filesystem for the embedded assets. If useLocal is true, the filesystem's contents are instead used.

func FSByte

func FSByte(useLocal bool, name string) ([]byte, error)

FSByte returns the named file from the embedded assets. If useLocal is true, the filesystem's contents are instead used.

func FSMustByte

func FSMustByte(useLocal bool, name string) []byte

FSMustByte is the same as FSByte, but panics if name is not present.

func FSMustString

func FSMustString(useLocal bool, name string) string

FSMustString is the string version of FSMustByte.

func FSString

func FSString(useLocal bool, name string) (string, error)

FSString is the string version of FSByte.

func FenCharToFigurine

func FenCharToFigurine(piece string) string

CharToUnicode converts a simple "FEN" character (that is, Q=white queen, p=black pawn, etc) into a single-rune unicode string containing the unicode figurine for that piece.

func GetFigurineName

func GetFigurineName(fenChar string) string

func GetImageName

func GetImageName(fenChar string, iCol int, iRow int) string

func GetImagePath

func GetImagePath(fenChar string, iCol int, iRow int, size int) string

func GetSquare

func GetSquare(iCol int, iRow int) string

func SimpleBoardFromFen

func SimpleBoardFromFen(fen string) []string

func UnicodeBoardFromFen

func UnicodeBoardFromFen(fen string) []string

func WriteBoardAsPng

func WriteBoardAsPng(fen string, size int, w io.Writer)

func WritePng

func WritePng(fen string, size int, filename string)

Types

type BoardUpdate

type BoardUpdate struct {
	Board *chess.Board
}

func NewBoardUpdate

func NewBoardUpdate(board *chess.Board) *BoardUpdate

BoardUpdate contains a full position update from the DGT board. Note that instead of making the Message rely directly on the board representation, we hide the specifics of the representation inside a small wrapper (BoardUpdate), which helps insulate the code from changes to the board representation.

func (*BoardUpdate) ToString

func (b *BoardUpdate) ToString() string

type Command

type Command struct {
}

type DgtBoard

type DgtBoard struct {

	// A channel for reading messages from the board.
	MessagesFromBoard chan *Message

	// A channel for sending commands to the board.
	CommandsToBoard chan *Command
	// contains filtered or unexported fields
}

func NewDgtBoard

func NewDgtBoard(portName string) *DgtBoard

NewBoard() ...

func (*DgtBoard) Close

func (dgtboard *DgtBoard) Close()

func (*DgtBoard) ReadLoop

func (dgtboard *DgtBoard) ReadLoop()

func (*DgtBoard) WriteBytes

func (dgtboard *DgtBoard) WriteBytes(bytes []byte) (int, error)

func (*DgtBoard) WriteCommand

func (dgtboard *DgtBoard) WriteCommand(command byte) (int, error)

type FieldUpdate

type FieldUpdate struct {
	Square chess.Sq
	Piece  chess.Piece
}

FieldUpdate encapsulates a raw field update from a DGT board. It's not a full chess move. It consists of detecting a piece (including the special piece EMPTY) moving into a square.

func NewFieldUpdate

func NewFieldUpdate(square chess.Sq, piece chess.Piece) *FieldUpdate

func (*FieldUpdate) PieceLetter

func (fu *FieldUpdate) PieceLetter() string

func (*FieldUpdate) ToString

func (fu *FieldUpdate) ToString() string

type InfoUpdate

type InfoUpdate struct {
	// contains filtered or unexported fields
}

func NewInfoUpdate

func NewInfoUpdate(info string) *InfoUpdate

func (*InfoUpdate) ToString

func (iu *InfoUpdate) ToString() string

type Message

type Message struct {
	BoardUpdate *BoardUpdate
	FieldUpdate *FieldUpdate
	TimeUpdate  *TimeUpdate
	InfoUpdate  *InfoUpdate
}

Message is really just a way to multiplex three different types of message onto a single channel.

func NewBoardUpdateMessage

func NewBoardUpdateMessage(boardUpdate *BoardUpdate) *Message

func NewFieldUpdateMessage

func NewFieldUpdateMessage(fieldUpdate *FieldUpdate) *Message

func NewInfoUpdateMessage

func NewInfoUpdateMessage(info string) *Message

func NewTimeUpdateMessage

func NewTimeUpdateMessage(timeUpdate *TimeUpdate) *Message

func (*Message) ToString

func (m *Message) ToString() string

Note, not implementing Stringer interface as you can't implement that on pointer receiver.

type MessageProcessor

type MessageProcessor struct {
	Board *chess.Board

	// The first piece lifted; we use this to help us
	// signal special configurations from the board.
	FirstPieceUp *FieldUpdate

	// Could use interface{}, yadda yadda
	PiecesInTheAir map[chess.Sq]chess.Piece
	PiecesDropped  map[chess.Sq]chess.Piece
}

func NewMessageProcessor

func NewMessageProcessor() *MessageProcessor

func (*MessageProcessor) Air

func (mp *MessageProcessor) Air() string

func (*MessageProcessor) AirState

func (mp *MessageProcessor) AirState() string

func (*MessageProcessor) Dropped

func (mp *MessageProcessor) Dropped() string

func (*MessageProcessor) GetPieceLifted

func (mp *MessageProcessor) GetPieceLifted(square chess.Sq) chess.Piece

GetPieceLifted returns the piece that was lifted from a square. We can't look directly into the Board because until we have decoded an entire move it doesn't get updated. Image the following sequence of meaningless and illegal physical piece moves by White:

e2f3, Ke2, Ke1, f3e2

At the end of the sequence, all the pieces have returned back to their starting square, meaning that White's next legal move *should* be recognised and accepted. However, when White moves his King from e2 back to e1, we need to "know" that the piece being lifted is the King, not the pawn originally on e2! Don't forget, we don't actually receive a message saying, "White King lifted"; we receive a messsage saying, "e2 is now empty", and we have to work out what piece was moved.

func (*MessageProcessor) GetSpecialAirState

func (mp *MessageProcessor) GetSpecialAirState() (chess.Sq, chess.Piece)

func (*MessageProcessor) IsMovePseudoLegal

func (mp *MessageProcessor) IsMovePseudoLegal() bool

func (*MessageProcessor) LogAirState

func (mp *MessageProcessor) LogAirState()

func (*MessageProcessor) ProcessMessage

func (mp *MessageProcessor) ProcessMessage(m *Message)

type TimeUpdate

type TimeUpdate struct {
}

TimeUpdate encapsulates a raw time update from a DGT board.

func NewTimeUpdate

func NewTimeUpdate() *TimeUpdate

func (*TimeUpdate) ToString

func (tu *TimeUpdate) ToString() string

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL