My next step was to integrate my rudimentary Novag Citrine interface with python-chess:
https://python-chess.readthedocs.io/en/v0.14.0/
The main 'glue' that was needed was code to convert moves from the Universal Chess Interface (UCI) format to the Novag format and vice versa. This module makes it easy to keep track of the position from within Python, and connect to external chess engines.
As noted previously, I found it necessary to send engine moves to the Citrine twice. A 0.1 second delay after each command worked well for non-captures. In this case, the piece to be moved was clearly indicated, as was the destination square when the piece was lifted. I found that this did not work well when the engine's move was a capture. In this case, the destination square flashed very briefly, and the piece to be moved was again indicated clearly, but the destination square was not indicated at all when the piece was lifted. I added another 3 second delay after sending the first capture, which gave more time to see which piece was being captured.
I can now play against the Stockfish engine on my Novag Citrine board.
The blog that seeks out hard evidence concerning chess training methods for the average player - particularly the not so young average player.
Saturday, 28 May 2016
Tuesday, 24 May 2016
Rudimentary Novag Citrine Interface
My next experiment was to write a rudimentary interface for the Novag Citrine, using Python 3. With this simple interface, the user has to type the moves of an external engine onto the command line. The Citrine's responses are also written to the command line. I found that it was necessary to impose a 0.1 second delay between commands, to prevent some of the commands being lost. As noted previously, moves have to be sent to the Citrine twice. It is necessary to wait for the user to reply. If the user takes back moves while the engine is thinking, the engine's move is discarded. If the user takes back the engine's move, and plays another move, the engine changes sides. Setting up the start position terminates the program, when "New Game" is received from the Citrine. Here is the program:
import serial, time
port = '/dev/ttyUSB0'
ser = serial.Serial(port, 57600, timeout=0.05)
print(ser.name, 'opened')
def send_command(command):
''' Send a command from the PC to the Citrine.
'''
ser.write((command + '\r\n').encode(encoding='UTF-8'))
time.sleep(0.1)
def make_move(move):
''' Tell the Citrine to make a move, and return True if a valid reply is
received. 'j' tells the Citrine to use its own engine to make the move.
'''
if move == 'j':
send_command('j')
else:
send_command('m' + move)
send_command('m' + move)
line = ser.readline().decode(encoding='UTF-8')[:-1]
print(line)
return line != '' and line[0] == 'M'
def get_reply():
''' Get the reply, which should be a move, optionally preceded by one or
more take backs. Return True if a valid reply is received.
'''
while True:
line = ser.readline().decode(encoding='UTF-8')[:-1]
if line != '':
print(line)
if line[0] != 'T':
break
time.sleep(0.1)
return line[0] == 'M'
send_command('u on') # Turn Referee Mode on.
send_command('x on') # Turn Xmit on.
send_command('l tr8') # Set the Level.
ser.flushInput() # Flush the input buffer.
while True:
move = input('Engine move? ')
if ser.inWaiting() == 0: # Check that the input buffer is empty
if not make_move(move):
break
if not get_reply():
break
ser.close()
Everything appears to work satisfactorily.
import serial, time
port = '/dev/ttyUSB0'
ser = serial.Serial(port, 57600, timeout=0.05)
print(ser.name, 'opened')
def send_command(command):
''' Send a command from the PC to the Citrine.
'''
ser.write((command + '\r\n').encode(encoding='UTF-8'))
time.sleep(0.1)
def make_move(move):
''' Tell the Citrine to make a move, and return True if a valid reply is
received. 'j' tells the Citrine to use its own engine to make the move.
'''
if move == 'j':
send_command('j')
else:
send_command('m' + move)
send_command('m' + move)
line = ser.readline().decode(encoding='UTF-8')[:-1]
print(line)
return line != '' and line[0] == 'M'
def get_reply():
''' Get the reply, which should be a move, optionally preceded by one or
more take backs. Return True if a valid reply is received.
'''
while True:
line = ser.readline().decode(encoding='UTF-8')[:-1]
if line != '':
print(line)
if line[0] != 'T':
break
time.sleep(0.1)
return line[0] == 'M'
send_command('u on') # Turn Referee Mode on.
send_command('x on') # Turn Xmit on.
send_command('l tr8') # Set the Level.
ser.flushInput() # Flush the input buffer.
while True:
move = input('Engine move? ')
if ser.inWaiting() == 0: # Check that the input buffer is empty
if not make_move(move):
break
if not get_reply():
break
ser.close()
Everything appears to work satisfactorily.
Friday, 20 May 2016
Connecting to the Novag Citrine using Python
I have been learning Python 3, and wrote a simple program to connect to the Novag Citrine:
import serial
port = '/dev/ttyUSB0'
ser = serial.Serial(port, 57600, timeout=0.05)
print(ser.name, 'opened')
print("When prompted, enter a Novag command, '' to read, or q to quit")
while True:
while True:
byteline = ser.readline()
if byteline == b'':
break
print(byteline.decode(encoding='UTF-8')[:-1])
command = input('Command? ')
if command == 'q':
break
elif command != '':
command += '\r\n'
ser.write(command.encode(encoding='UTF-8'))
ser.close()
I tested this program on Ubuntu Linux. It should also work on Windows, a Mac and any other platform that supports Python 3. The variable "port" has to be set to the serial port that you are using to connect to the Citrine. Here is a sample session, in which the moves for white are entered on the Citrine, and the moves for black are sent from the PC:
/dev/ttyUSB0 opened
When prompted, enter a Novag command, '' to read, or q to quit
Command?
New Game
Command? u on
.Referee on
Command? x on
Xmit on
Command? l tr8
Level tr 8
Command?
M 1 e2-e4
Command? me7e5
Command? me7e5
M 1, e7-e5
Command?
M 2 g1-f3
Command? mb8c6
Command? mb8c6
M 2, b8-c6
Command?
M 3 d2-d4
Command? me5d4
Command? me5d4
M 3, e5xd4
Command?
M 4 d1xd4
Command?
T 4 d1xd4
Command?
M 4 f3xd4
Command? q
Everything appears to work. After entering a move for white on the Citrine, enter an empty line as a command to read the reply from the PC. As noted previously, it is necessary to send the moves for black twice from the PC. Typing 'q' ends the session. Responses from the Citrine are buffered, and can be read after more commands have been sent.
import serial
port = '/dev/ttyUSB0'
ser = serial.Serial(port, 57600, timeout=0.05)
print(ser.name, 'opened')
print("When prompted, enter a Novag command, '' to read, or q to quit")
while True:
while True:
byteline = ser.readline()
if byteline == b'':
break
print(byteline.decode(encoding='UTF-8')[:-1])
command = input('Command? ')
if command == 'q':
break
elif command != '':
command += '\r\n'
ser.write(command.encode(encoding='UTF-8'))
ser.close()
I tested this program on Ubuntu Linux. It should also work on Windows, a Mac and any other platform that supports Python 3. The variable "port" has to be set to the serial port that you are using to connect to the Citrine. Here is a sample session, in which the moves for white are entered on the Citrine, and the moves for black are sent from the PC:
/dev/ttyUSB0 opened
When prompted, enter a Novag command, '' to read, or q to quit
Command?
New Game
Command? u on
.Referee on
Command? x on
Xmit on
Command? l tr8
Level tr 8
Command?
M 1 e2-e4
Command? me7e5
Command? me7e5
M 1, e7-e5
Command?
M 2 g1-f3
Command? mb8c6
Command? mb8c6
M 2, b8-c6
Command?
M 3 d2-d4
Command? me5d4
Command? me5d4
M 3, e5xd4
Command?
M 4 d1xd4
Command?
T 4 d1xd4
Command?
M 4 f3xd4
Command? q
Everything appears to work. After entering a move for white on the Citrine, enter an empty line as a command to read the reply from the PC. As noted previously, it is necessary to send the moves for black twice from the PC. Typing 'q' ends the session. Responses from the Citrine are buffered, and can be read after more commands have been sent.
Subscribe to:
Comments (Atom)