MagneticSensorChessBoard1987
Jump to navigation
Jump to search
Click here to comment see PlayChessWithAWebCam
History
During my study at RWTH Aachen from 1982 to 1989 I (Wolfgang Fahl) used to play chess every once in a while against my fellow students. One of them was much better and therefore told me when i made a wrong move (mostly a few moves later). He would then allow me to take back the bad move. For that we had to remember what the moves where. Sometimes this what not so easy so we came up with the idea to create a chess board that would help us track the moves.
Requirements
- The board should be as "non-invasive" as possible - it should not modify the game of chess more than necessary
- The solution should track moves of peaces and record them in a text file
- The solution should only allow valid moves
- The solution should be able to work from a predefined chess position
- The board should connect to a regular IBM PC
Design
- a 335 x 335 mm chess plan with 35 mm squares was used as a basis
- 32 wooden chess pieces with a height of 33 mm each were fitted with magnets from a Brio-Bahn
- The Centronics Printer port would be used for connecting
- There would be control leds for each row/column of the board with which to indicate a move detected or to be done by the human player
- Since the centronics port has only a few available input lines and max 8 output lines TTL multiplexer/demultipler chips would be used
Hardware
Front
Back
Connectors
Schematics
Parts:
Partlist
- chess plan 35 mm fields
- 64 Reed Contacts
- 16 Black Chess Pieces
- 16 White Chess Pieces
- 32 magnets
- 17 red LEDs
- On/Off switch
- Male Sub-D Connector 15
- Adapter cable Centronics - SUB-D 15 Female
- Copper Tape
- Aluminum Frame
- 1 TTL 74151
- 1 TTL 74154
- 330 Ohm Resistor
- Batteryholder for 3 x AA
Software
The Software was written with Borland Turbo Pascal
SCHFELD.PAS
PROGRAM Schachbrett_Ansteuerung(INPUT,OUTPUT);
TYPE
KBrettTyp= ARRAY [0..7,0..7] OF BOOLEAN;
CONST
BlinkTime = 100;
MaxBlinkTime = 1400;
MaxTime = 1700;
VAR
Pw,
Count,LED1,LED2,
LED3,LED4,MovCount,
FigMovCnt: INTEGER;
MovList : ARRAY[0..500] OF LSTRING(5);
KBrett,SBrett : KBrettTyp;
ZugDatei : TEXT;
(*$include: 'screen.def'*)
(*$include: 'string.def'*)
PROCEDURE ClrLED;
BEGIN
LED1:=#80;
LED2:=LED1;
LED3:=LED1;
LED4:=LED1;
END;
FUNCTION CheckBrett(D,L: INTEGER): BOOLEAN; EXTERN;
PROCEDURE Sound(Freq,Time: INTEGER); EXTERN;
FUNCTION Check(x,y: INTEGER):BOOLEAN;
VAR
Pw,Dw,P1,P2:INTEGER;
BEGIN
CASE (Count MOD 2) OF
0: BEGIN P1:=LED1; P2:=LED3; END;
1: BEGIN P1:=LED2; P2:=LED4; END;
END;
IF Count<MaxBlinkTime THEN
BEGIN
CASE (Count DIV BlinkTime) MOD 4 OF
0,1,2: Pw:=P1;
3: Pw:=P2;
END;
END
ELSE
Pw:=P2;
IF Count<MaxBlinkTime THEN
Count:=SUCC(Count)
ELSE
IF Count<MaxTime THEN
BEGIN
IF FigMovCnt>=1 THEN
Count:=0
ELSE
Count:=SUCC(Count);
END
ELSE
ClrLED;
Dw:=(x * 8) +y;
Check:=CheckBrett(Dw,Pw);
END;
PROCEDURE State(VAR B:KBrettTyp);
VAR
Spalte,Zeile: INTEGER;
BEGIN
FOR Spalte:=0 TO 7 DO
FOR Zeile:=0 TO 7 DO
B[Spalte,Zeile]:=Check(Spalte,Zeile);
END;
PROCEDURE Kontakte;
VAR
Spalte,Zeile: INTEGER;
BEGIN
CLRSCR;
ClrLED;
REPEAT
GOTOXY(1,1);
FOR Spalte:=0 TO 7 DO
BEGIN
FOR Zeile:=0 TO 7 DO
BEGIN
IF KeyPressed THEN RETURN;
IF Check(Spalte,Zeile) THEN WRITE('*') ELSE WRITE('.');
END;
WRITELN;
END;
UNTIL FALSE;
END;
PROCEDURE Scribe(VAR D:TEXT;P:INTEGER);
BEGIN
WRITE(D,MovList[P]);
CASE (P MOD 4) OF
0: BEGIN WRITELN(D);WRITE(D,P DIV 4+1:3,': '); END;
2: WRITE(D,' ');
OTHERWISE
;
END;
END;
PROCEDURE Zuege;
PROCEDURE Accept(X1,Y1,X2,Y2: INTEGER);
PROCEDURE StatePos(Zeile,Spalte: INTEGER);
BEGIN
MovCount:=SUCC(MovCount);
CONCAT(MovList[MovCount],CHR(ORD('A')+Zeile));
CONCAT(MovList[MovCount],CHR(ORD('8')-Spalte));
CASE (MovCount MOD 4) OF
1,3: BEGIN
IF FigMovCnt>=2 THEN
CONCAT(MovList[MovCount],' X ')
ELSE
CONCAT(MovList[MovCount],' - ');
END;
OTHERWISE
;
END;
Scribe(OutPut,MovCount);
END;
BEGIN
StatePos(X1,Y1);
StatePos(X2,Y2);
Count:=0;
State(SBrett);
Sound(800,20);
END;
PROCEDURE ZugVerfolgen;
VAR
AktCheck: BOOLEAN;
P1,P2,P3,P4,Spalte,Zeile: INTEGER;
PROCEDURE GetPos(VAR X,Y,L1,L2: INTEGER);
BEGIN
X:=Zeile;
Y:=Spalte;
L1:=(X+8) * 8;
L2:=Y * 8;
Count:=0;
State(SBrett);
END;
BEGIN
WRITELN('Zug-Anzeige: ');
Scribe(OutPut,0);
MovCount:=0;
State(SBrett);
FigMovCnt:=0;
ClrLED;
REPEAT
FOR Zeile:=0 TO 7 DO
FOR Spalte:=0 TO 7 DO
BEGIN
IF KeyPressed THEN RETURN;
AktCheck:=Check(Spalte,Zeile);
IF (NOT AktCheck) AND Sbrett[Spalte,Zeile] THEN
BEGIN { Figur Entfernt }
IF FigMovCnt=1 THEN
BEGIN
GetPos(P3,P4,LED3,LED4);
FigMovCnt:=FigMovCnt+1;
END
ELSE
BEGIN
GetPos(P1,P2,LED1,LED2);
LED3:=LED1;
LED4:=LED2;
FigMovCnt:=FigMovCnt+1;
END;
END;
IF AktCheck AND (NOT Sbrett[Spalte,Zeile]) THEN
BEGIN { Figur gesetzt }
CASE FigMovCnt OF
0: BEGIN
GetPos(P1,P2,LED1,LED2);
END;
1: BEGIN
IF NOT ((P1=Zeile) AND (P2=Spalte)) THEN { Figur bewegt? }
BEGIN
Accept(P1,P2,Zeile,Spalte);
GetPos(P1,P2,LED3,LED4);
LED1:=LED3;LED2:=LED4;
FigMovCnt:=0;
END
ELSE
BEGIN
ClrLED;
State(SBrett);
FigMovCnt:=0;
END;
END
OTHERWISE
IF (P1=Zeile) AND (P2=Spalte) THEN
BEGIN
Accept(P3,P4,Zeile,Spalte);
GetPos(P1,P2,LED3,LED4);
FigMovCnt:=0;
END
ELSE
BEGIN
Accept(P1,P2,Zeile,Spalte);
GetPos(P1,P2,LED3,LED4);
FigMovCnt:=0;
END;
END; { Case }
END;
END;
UNTIL FALSE;
END;
VAR
Taste: CHAR;
z: INTEGER;
BEGIN
CLRSCR;
ZugVerfolgen;
ASSIGN(ZugDatei,'SCHParti.txt');
REWRITE(ZugDatei);
FOR z:=0 TO MovCount DO
Scribe(ZugDatei,z);
CLOSE(ZugDatei);
END;
VAR
Taste: CHAR;
BEGIN
Window(1,1,80,25);
REPEAT
CLRSCR;
WRITELN('(K)ontakt Matrix');
WRITELN('(Z)ug-Anzeige');
GetKey(Taste);
MovList[0]:=NULL;
Taste:=UpCase(Taste);
CASE Taste OF
'K': Kontakte;
'Z': Zuege;
OTHERWISE
;
END;
UNTIL Taste='E';
END.
Centronics Port Check
DATA SEGMENT PUBLIC 'DATA'
DATA ENDS
DGROUP GROUP DATA
ASSUME CS:ADDS,DS:DGROUP,SS:DGROUP
ADDS SEGMENT 'CODE'
PUBLIC CheckBrett,Sound
Sound PROC FAR ; Freq,Time: INTEGER;
PUSH BP
MOV BP,SP
PUSH DI
MOV DI,8[BP] ; Freq;
MOV BX,6[BP] ; Time;
MOV AL,0B6H ; Zeitgeber Modus-Register schreiben
OUT 43H,AL
MOV DX,14H ; Zeitgeber Divisor =
MOV AX,4F38H ; 1331000/Frequenz
DIV DI
OUT 42H,AL ; Zaehler in niederw. Byte Timer 2
MOV AL,AH
OUT 42H,AL ; Zaehler in hoeherw. Byte Timer 2
IN AL,61H ; Aktuelle Einstellung Port B
MOV AH,AL ; In AH Sichern
OR AL,3 ; Lautsprecher einschalten
OUT 61H,AL
Warte:
MOV CX,2801 ; 10 Millisekunden warten
LS_Ein:
LOOP LS_Ein
DEC BX ; Einschaltzaehler auf 0?
JNZ Warte ; wenn nicht Lautsprecher anlassen
MOV AL,AH ; Port wiederherstellen
OUT 61H,AL
POP DI
POP BP
RET 4
Sound ENDP
CheckBrett PROC FAR ;d,l: INTEGER: BOOLEAN
PUSH BP
MOV BP,SP
CLI
MOV AX,8[BP] ; d
MOV CX,6[BP] ; l
MOV DX,0278h
OUT DX,AL ; Port[Data]:=D
INC DX
IN AL,DX
XCHG AL,CL
DEC DX
OUT DX,AL ; Port[Data]:=l
STI
MOV AX,0
AND CL,080h
JNZ FALSE
INC AX
FALSE:
POP BP
RET 4
CheckBrett ENDP
ADDS ENDS
END