25 Years of Programming
An open source source for C, C++, OWL, BASIC, MDB, XLS, DOT, and more...
Home   Projects   Up   Sitemap   Search   Blog   Forum+Chat   About Us   Privacy   Terms of Use   Feedback   FAQ   Images   Services   Payments   Humor   Music

Chess board program - Borland C++ OWL

Uses a Borland ObjectWindows Library (OWL 2.0) TLayoutWindow to create an invertible chess board with pieces on it. As you move the pieces, it creates a record of the game which can be saved to disk, reloaded, and animated. The program does not, however, play chess, but you can play a game on the board it provides. It's not exactly a breakthrough in chess programming, but it does demonstrate some useful OWL methods. The program:

  1. Shows how to use the capabilities of TLayoutWindow.
  2. Shows how to do useful things in the IdleAction function.
  3. Has unusual methods for determining which squares pieces can legally move to.
  4. Has a modular design, with functions and variables pushed down to the lowest level objects possible.

Download

The zip file contains:

  • Chess.cpp - the chess board program
  • Chess.def - Windows module definition file for the project
  • Chess.rh - resource identifier header file
  • Chess.rc - Windows resources for the Resource Compiler
  • Chess.rtf - a skeletal .RTF file for the WinHelp file for this project. There's not much in it, but it's something to build on. It is based on my HELPFILE.DOT Word template.
  • Chess.hpj - instructions to the Help Compiler for building the WinHelp file
  • Chess.bmp - a bitmap of the chess board layout, used while designing the color scheme
  • Chess.xls - a worksheet pre-formatted for creating a printable annotated game listing, and a few other mostly useless worksheets. It's in Excel 5.0 format, which should import without problems to any later version.
  • Copying.txt - GNU GPL license

Download chess.zip, about 54 KB.


Screenshot:

This is actually just the chess.bmp file described above, but it is basically what the board looks like when the program is running. The green square indicates which side is to move. The 41's are the starting piece strengths of each side. To move a piece, you click on it, then click the square you want to move it to.

Image showing what the Chess.cpp game board looks like


Other Chess links

Free Internet Chess Server
Great place to play Chess online! You can also watch games, learn about Chess, and chat. They have download links for graphical interfaces to use for playing, one of which is so fantastic, I'm giving it a link here...
 
BabasChess - The Free Internet Chess Client
Beautiful and full featured chess client (interface) for playing online or against your computer. You can also study and animate saved games. It comes with the Crafty chess engine. This program is so beautiful you should look at its interface even if you don't play or like Chess (just kidding, but it is that nice).
 
WinBoard chess client
This is another good chess client. I've used it for playing offline, for studying saved games and for pitting two chess engines against each other. It comes with both the GNU Chess 4 and 5 engines, and its homepage has lots of chess and engine information, and links.
 

CHESS.CPP

/*	chess.cpp			11-29-01
	Copyright (C)1997-2001 Steven Whitney.
	Initially published by http://25yearsofprogramming.com.

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	Version 3 as published by the Free Software Foundation.
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

Borland C++ 4.0 OWL (ObjectWindows Library). Uses Borland BIDS container classes.

Maintains and displays a chessboard, keeps track of the 2 players,
accepts (only legal) moves.

------
To Do:

The last time I ran this, there was something wrong with the timer.  It started
ringing immediately and couldn't be stopped. I think I had made some mods to the
timer routine so I could use it as a cooking timer, and had failed to test what
effects that might have on its use within the program. OR the changes might have
been made to the Stopwatch class, which could have affected this program in ways
I didn't anticipate.

bring in a small SDib, loaded from chess.bmp(?), so I can use its color table instead
of the Colors[] array below.  Add routines so user can change the colors while
program is running; then write back to chess.bmp at exit.
Figure out why the current font & color usage, which was the best I could find after a
lot of experimenting, is still so hard on the eyes.

program should start out as a chess utility (display device and stats calculator),
not as a player.  Later, as a player, it can use its own utility routines.

design a simple Rating system for moves.
------
a user-entered move is legal if it's found in Moves[].  add BOOL checkusermoves.
if it is off, no checking, to allow setting up a board position.

12/22/05: I think at one point I intended this program to start by playing random moves and
reward or punish itself based on outcomes like the tictac.cpp program,
but it never got that far, and I think that approach would have been hopeless for chess.

Allow Player to be human, random, or LEARNER (same as tictac).
The emphasis here should be *learning*, so the program can have the potential
to play better than I can -- to accumulate on its own more knowledge than
I could ever build into it.  This raises, yet again, the problem of how to
conceive, for example, novel statistical measures that I haven't programmed in --
innovation.  This is a possible use of my proposed Random Function Generator,
discussed elsewhere, maybe in complex.doc or stats.doc.

it should:
be able to display any current game statistics, OR square statistics (in the
squares in smaller type, along the the piece marker -- alternate display mode),
OR piece statistics (squares guarded, etc.)
(stats display PLUS backing up capability allows comparing proposed moves)
be able to play random, but legal, moves.  Random player will be used to
train the learner.

While the master board tracks the real game, Player must have the ability to
construct an entire duplicate of it with only one (or more) moves changed,
to determine how the move would change the situation.  (ChessBoard needs a
copy constructor.  Also make a ctor that takes only a 64-char string as its
argument, black lower case, white upper.)  For farther lookahead, Player may
also need to be able to take the other side, to determine what move it is
likely to play.

don't exclude King captures (PxK) from *proposed* Moves array: if any exist against you,
you know you're in check.

allow an alternate display, coloring square backgrounds by some measure of side dominance

in reversing an en passant capture, you can put the captured pawn on the
capture square without problem, because in will just go back 1 more on the next move.
(but what if that's where you stop reversing moves?)

------
Notes:
--Invalidate(TRUE) is required for the Squares.  Haven't tested all uses of it, though.
--The timer seems much more accurate than Sched+.  It accurately timed 20 minutes
  while I did other things in several running apps.

*/
#include <owl\owlpch.h>
#include <owl\chooseco.h>
#include <owl\opensave.h>
#include <owl\radiobut.h>
#include <owl\edit.h>
#include <owl\inputdia.h>
#include <owl\validate.h>
#include <classlib\arrays.h>
#include <stdio.h>
#include <io.h>
#include <iomanip.h>
#include <complex.h>
#include <bwcc.h>
#include "c:\bcs\my.h"
#pragma hdrstop

#include "c:\bcs\mylib.cpp"
#include "c:\bcs\library\filearay.cpp"
#include "c:\bcs\library\stopwatc.cpp"
#include "chess.rh"

static const char AppName[] = "Chess";

//////////////////////////////////////////////////////////////////////////////
// GLOBAL VARIABLES
//----------------------------------------------------------------------------
// BLACK, WHITE = piece sides, or ends of the board, MUST be 0,1
// (used as array indexes and loop counters)
// QUEENSIDE, KINGSIDE = sides of the board, for castling.
// the group of 4 can also be used to describe board vantage points
enum { BLACK, WHITE, QUEENSIDE, KINGSIDE};

// TO ADJUST QUICKLY, USE CHESS.BMP, loaded into Winbrot to edit the colors.
// I've modified these slightly so all are unique.
TColor Colors[16] =			// array of 16 TColor objects
{
	TColor(128,128,128),    // 0  - Black Square Background
	TColor(192,192,192),    // 1  - White Square Background
	TColor(000,000,000),    // 2  - Black Piece Text
	TColor(254,254,254),    // 3  - White Piece Text
	TColor(000,000,255),    // 4  - Selected Black Piece Text
	TColor(255,000,000),    // 5  - Selected White Piece Text
	TColor(253,253,253),    // 6  - Vert/Horiz Grid Label Background
	TColor(000,000,220),    // 7  - Vertical   Grid Label Text
	TColor(180,000,000),    // 8  - Horizontal Grid Label Text
	TColor(000,255,000),    // 9  - GO Square Background (when indicating GO)
	TColor(000,000,000),    // 10 - GO Square Text (there currently isn't any)
	TColor(252,252,252),    // 11 - PieceScore Background (and GO, when not indicating GO)
	TColor(000,000,000),    // 12 - PieceScore Text
	TColor(255,255,255),    // 13 - Main Window Background
	TColor(000,000,000),    // 14 - (unused)
	TColor(000,000,000)     // 15 - (unused)
};

//////////////////////////////////////////////////////////////////////////////
// a chess piece.  It holds a lot of data, but is relatively inanimate.
class Piece
{
public:
	// default values are arbitrary, not for use
	Piece(int side = BLACK, int type = 'P', int index = 0, int twin = 0,
			TPoint loc = TPoint(0,0));
	Piece(const Piece& other);

	Piece& operator = (const Piece& other);
	BOOL operator == (const Piece& other) const { return(&other == this); }
	BOOL operator < (const Piece& other) const { return(Value < other.Value); }

	friend ostream& operator << (ostream&, const Piece&);  	// write
	friend istream& operator >> (istream&, Piece&);			// read

	// functions
	void MoveTo(const TPoint& p);	// move to point p and archive the loc
	int UnMove();					// undo the last move
	int MoveCount();     			// number of times this piece has moved
	int OtherSide();				// the other side from this side: WHITE or BLACK
	static int OtherSide(int side);	// static version, for use by anyone

	// variables
	int Side;                       // BLACK or WHITE
	int Type;						// one of: BKNPQR
	int Value;						// point value
	int Index;						// its own index (i) in Pieces[Side][i]
	int Twin;						// for RNB, index of its twin, else 0
									// for use in determining move notation ambiguities
	TPoint Loc;						// what board square it occupies
									// when captured, RETAIN its last value, for undo.
	int ThreatCount;              	// number of opposing pieces it is threatened by
	int CapturedBy;					// the index of the piece that captured it, or 0
	TArrayAsVector<TPoint> Trail;	// its trail of Locs, includes its current Loc
	TArrayAsVector<TPoint> Guards;	// list of squares it guards
	TArrayAsVector<TPoint> Moves;	// squares it thinks it can move to (physically legal)

protected:
};
//----------------------------------------------------------------------------
// constructor
Piece::Piece(int side, int type, int index, int twin, TPoint loc) :
	Trail(10,0,10), Moves(10,0,10), Guards(10,0,10),
	Side(side), Index(index), Twin(twin), CapturedBy(0)
{
switch(Type = toupper(type))
{
	case 'P': Value = 1; break; case 'N': Value = 3; break; case 'B': Value = 3; break;
	case 'R': Value = 5; break; case 'Q': Value = 9; break; case 'K': Value = 2; break;
	// early on, I decided king must have a point value (don't remember why)
	// 2 is its relative move & capturing power (slightly more than a pawn)
}
MoveTo(loc);
}                      			//constructor
//----------------------------------------------------------------------------
// copy constructor
Piece::Piece(const Piece& other) :
	Trail(max(other.Trail.GetItemsInContainer(),10u),0,10),
	Moves(max(other.Moves.GetItemsInContainer(),10u),0,10),
	Guards(max(other.Guards.GetItemsInContainer(),10u),0,10)
{ *this = other; }
//----------------------------------------------------------------------------
Piece& Piece::operator = (const Piece& other)
{
if(this == &other)
	return(*this);
Side = other.Side;
Index = other.Index;
Twin = other.Twin;
Type = other.Type;
Value = other.Value;
Loc = other.Loc;
ThreatCount = other.ThreatCount;
CapturedBy = other.CapturedBy;

int i;
Trail.Flush(); Moves.Flush(); Guards.Flush();
for(i = 0 ; i < other.Trail.GetItemsInContainer() ; i++)  Trail.Add(other.Trail[i]);
for(i = 0 ; i < other.Moves.GetItemsInContainer() ; i++)  Moves.Add(other.Moves[i]);
for(i = 0 ; i < other.Guards.GetItemsInContainer() ; i++) Guards.Add(other.Guards[i]);

return(*this);
}                    			// operator =
//----------------------------------------------------------------------------
ostream& operator << (ostream& os, const Piece& p)
{
return(os);
}                        		//operator << write
//----------------------------------------------------------------------------
istream& operator >> (istream& is, Piece& p)
{
return(is);
}                     			//operator >> read
//----------------------------------------------------------------------------
int Piece::OtherSide(int side) { return((side == BLACK) ? WHITE : BLACK); }
int Piece::OtherSide() { return(OtherSide(Side)); }
int Piece::MoveCount() { return(Trail.GetItemsInContainer() - 1); }
//----------------------------------------------------------------------------
// move to point p and archive the loc to its Trail
void Piece::MoveTo(const TPoint& p)
{
Loc = p;
Trail.Add(Loc);  				// Trail includes the point it is now ON
ThreatCount = 0;				// convenient to reset here
Moves.Flush(); Guards.Flush();  // all its stats will change
}								//MoveTo
//----------------------------------------------------------------------------
// undo this piece's last move: go to previous Loc and delete current Loc from trail
// returns 1 if it succeeded, else 0 (or could encode reason)
int Piece::UnMove()
{
int N = Trail.GetItemsInContainer();
if(!N)
	return(0);
Loc = Trail[N - 2];             // go to its previous Loc
Trail.Destroy(N - 1);  			// delete its current Loc from Trail
ThreatCount = 0;
Moves.Flush(); Guards.Flush();
// CapturedBy;					// reminder that caller must reset this, if necessary
return(1);
}								//UnMove
//----------------------------------------------------------------------------
// 						end class Piece
//////////////////////////////////////////////////////////////////////////////
// describes a move that a piece can make, whether a capture results, and a rating.
// it can be a potential move OR an actual move from this or a loaded game.
// Depending on the usage, some members might not have usable or knowable values.
class Move
{
public:
	Move(Piece* p = 0,TPoint from = TPoint(0,0),TPoint to = TPoint(0,0),
								Piece* captured = 0, int castletype = 0);
	Move(const Move& other) { *this = other; }

	Move& operator = (const Move& other);
	BOOL operator == (const Move& other) const;
	BOOL operator < (const Move&) const;

	friend ostream& operator << (ostream&, const Move&);  	// write
	friend istream& operator >> (istream&, Move&);			// read

	// THESE 2 REPLACE THE OLD PIECE* WHO,
						// MAKING A MOVE USABLE BY ANY CHESSBOARD.
	int Side;			// which side can make the move
	int Who;			// index: the piece is Pieces[Side][Who]
	TPoint From;		// needed for reference, output, archiving
	TPoint To;			// location piece can move to
	int CapSide;		// side of the piece being captured (else undefined, usu. 0)
	int Captured;		// piece that can be captured there, if any (or 0: you CAN test this)
	int Rating;			// score, used for determining best move
	int CastleType;		// flag for castling moves = 0, QUEENSIDE, or KINGSIDE
						// castle move: Who=King, Captured=Sameside Rook, CastleType=nonzero
						// NOTE: TO CASTLE, CLICK ON THE KING, THEN THE ROOK.
protected:
};
//----------------------------------------------------------------------------
// constructor
Move::Move(Piece* p, TPoint from, TPoint to, Piece* captured, int castletype) :
	From(from), To(to), Rating(0), CastleType(castletype)
{
Side = (p ? p->Side : 0);
Who =  (p ? p->Index : 0);
CapSide =  (captured ? captured->Side : 0);
Captured = (captured ? captured->Index : 0);
}								//constructor
//----------------------------------------------------------------------------
// used for looking up moves in the game record
BOOL Move::operator == (const Move& other) const
{
return((Side == other.Side) && (Who == other.Who) &&
		(From == other.From) && (To == other.To) && (CastleType == other.CastleType));

// Captured and CapSide?  no, because when searching for a Move, you probably
// don't know who may have been captured there.

}                      			//operator ==
//----------------------------------------------------------------------------
BOOL Move::operator < (const Move& other) const
{
return(Rating > other.Rating);	// descending: highest-rated is at [0]
}                       		//operator <
//----------------------------------------------------------------------------
Move& Move::operator = (const Move& other)
{
if(this == &other)
	return(*this);

Side = other.Side;
Who = other.Who;
From = other.From;
To = other.To;
CapSide = other.CapSide;
Captured = other.Captured;
Rating = other.Rating;
CastleType = other.CastleType;

return(*this);
}								// operator =
//----------------------------------------------------------------------------
// a Move only understands TPoint format, and has no access to the ChessBoard or Squares,
// but the Pieces hold info about themselves that you can use.
// The TPoints are piece Locs (Board-referenced, not Square).
ostream& operator << (ostream& os, const Move& m)
{
os << m.Side << " " << setw(2) << m.Who << " " << m.From << m.To << " "
   << m.CapSide << " " << setw(2) << m.Captured << " " << m.CastleType << " " << m.Rating;
return(os);
}                        		//operator << write
//----------------------------------------------------------------------------
istream& operator >> (istream& is, Move& m)
{
is >> m.Side >> m.Who >> m.From >> m.To >> m.CapSide >> m.Captured >> m.CastleType >> m.Rating;

// prevent loading invalid moves:
if( ((m.Side != BLACK) && (m.Side != WHITE)) ||
	(m.Who < 1) || (m.Who > 16) ||
	((m.CapSide != BLACK) && (m.CapSide != WHITE)) ||
	(m.Captured < 0) || (m.Captured > 16) ||
	((m.CastleType != QUEENSIDE) && (m.CastleType != KINGSIDE) && (m.CastleType != 0)) )
	{
		while(is.ignore());		// use up the stream to force failure
	}
return(is);
}                     			//operator >> read
//----------------------------------------------------------------------------
// 						end class Move
//////////////////////////////////////////////////////////////////////////////
class ChessBoard
{
public:
	ChessBoard();
	// copy ctor for hypothetical copies is the reason ChessBoard is separate from SChessWindow
	ChessBoard(const ChessBoard& other);
// 	ChessBoard(const string& b);			// construct from a 64-char string
	~ChessBoard();

	ChessBoard& operator = (const ChessBoard& other);
	BOOL operator == (const ChessBoard&) const;
	BOOL operator < (const ChessBoard&) const;
	operator string() const;

	friend ostream& operator << (ostream&, const ChessBoard&);  	// write
	friend istream& operator >> (istream&, ChessBoard&);			// read

	// functions
	void ResetPieces();					// reinitialize Pieces, and to their starting locs
	void Reset();						// reset everything to startup state
	Piece* Occupant(const TPoint& p);	// which piece occupies the logical board square
	int IsLegal(Piece*, TPoint p);		// whether piece can legally move to point p
	int IsLegal(Move&);					// whether the proposed move is legal
	BOOL PostMove(const Move&, BOOL newmove);			//
	BOOL UndoMove();					// undo last move, restoring any captured piece
	BOOL RedoMove();					// restore latest undone move, if any
	void Tabulate();					// all possible legal moves, tabulate board stats
	void FindPawnMoves(Piece*);
	void FindRookMoves(Piece*);
	void FindKnightMoves(Piece*);
	void FindBishopMoves(Piece*);
	void FindKingMoves(Piece*);
	static BOOL IsOnBoard(const TPoint& p);
	int CanCastle(int side, int KQside);	// returns 1 if ok, OR NEGATIVE if not

	// variables
	TArrayAsVector<Move> Game;		// the moves that constitute this game
	TSArrayAsVector<Move> Moves;	// all possible moves of all pieces at a given time
									// Pieces (or sides) can copy subsets of it, if needed
	int PieceScore[2];				// pt totals of pieces on board, index as [BLACK],[WHITE]
	int SideToMove;					// BLACK or WHITE
	int MoveIndex;					// into Game[], (index of latest move made + 1),
									// starts at 0, then after first move == 1, etc.
									// MoveIndex (NOT Game.GetItems) tells you to what
									// point the game is played: its current state.

	Piece Pieces[2][17];			// pieces start at Pieces[side][1], not [side][0]
									// that is, for each side, Pieces[0] is a dummy,
									// so that an index of 0 means "none", so you
									// can test it for zero, like a Piece* of 0.
									// yet if a 0 is accidentally used, pgm won't crash.

	// Alternative access methods into Pieces[][] that should be legal,
	// though I don't currently use them:
	Piece* BlackPieces;				// 1-dim array[17] dummy at [0], the rest [1]to[16]
	Piece* WhitePieces;				// 1-dim array[17] dummy at [0], the rest [1]to[16]
	Piece* AllPieces;				// 1-dim array[34] all pieces contiguous,
									// Black dummy at [0], White dummy at [17]
									// AllPieces could allow writing a single value
									// for .Who and .Captured in Move::operator <<
									// (i.e. saving their indexes without their Side)
protected:
};
//----------------------------------------------------------------------------
// constructor
ChessBoard::ChessBoard() : Moves(100,0,100), Game(100,0,100)
{
BlackPieces = &Pieces[0][0]; WhitePieces = &Pieces[1][0]; AllPieces = &Pieces[0][0];

ResetPieces();
}							//constructor
//----------------------------------------------------------------------------
// copy constructor
ChessBoard::ChessBoard(const ChessBoard& other) : Moves(100,0,100), Game(100,0,100)
{
	*this = other;
}							//copy constructor
//----------------------------------------------------------------------------
ChessBoard& ChessBoard::operator = (const ChessBoard& other)
{
if(this == &other)
	return(*this);

// several members are missing here
// 2006: I apparently wasn't yet using this operator =, so it doesn't matter that they're missing.
// MoveIndex
// #error must copy each array once format is finalized
// TArrayAsVector<Move> Game;
// TArrayAsVector<Move> Moves;
// Pieces[];

return(*this);
}
//----------------------------------------------------------------------------
// destructor
ChessBoard::~ChessBoard()
{
}
//----------------------------------------------------------------------------
// writes the sequential moves that made up the game.
ostream& operator << (ostream& os, const ChessBoard& c)
{
// writing starting Piece locs in string format might allow a listing to start mid-game,
// but is more complicated than it's worth.

int movecount = c.Game.GetItemsInContainer();
os << movecount << endl;
for(int i = 0 ; i < movecount ; i++)
	os << c.Game[i] << endl;

os << endl << (string)c << endl;		// ending board config
return(os);
}                      		//operator << write
//----------------------------------------------------------------------------
istream& operator >> (istream& is, ChessBoard& c)
{
c.Reset();					// reset everything to startup state
int movecount = 0;
is >> movecount;
Move m;
for(int i = 0 ; is && (i < movecount) ; i++)
	if(is >> m)        		// this does read (and overwrite) ALL m's members
		c.Game.Add(m);    	// #ERROR YOU COULD C.POSTMOVE(M)
return(is);
}                  			//operator >> read
//----------------------------------------------------------------------------
// returns board position as a text string 8x8 grid (with embedded \n),
// easy for copy and paste to or from a TEdit, or printing.
ChessBoard::operator string() const
{
string s('-',64);         						// start with all spaces (dashes)
for(int side = BLACK ; side <= WHITE ; side++)
	for(int i = 1 ; i < 17 ; i++)
	{
		const Piece& p = Pieces[side][i];
		if(!p.CapturedBy && IsOnBoard(p.Loc))
			s[8 * (7 - p.Loc.y) + p.Loc.x] = (char)(side == WHITE ? p.Type : tolower(p.Type));
	}
for(int i = 64 ; i > 0 ; i -= 8)
	s.insert(i, "\n");
return(s);
}                     			//operator string
//----------------------------------------------------------------------------
// whether a given point is on the board
BOOL ChessBoard::IsOnBoard(const TPoint& p)
{
static const TRect BoardRect(0,0,8,8);
return(BoardRect.Contains(p));
}
//----------------------------------------------------------------------------
// reinitialize only the pieces to their startup state, including starting locs.
// separate fn so you can animate the same game repeatedly, resetting only the pieces.
void ChessBoard::ResetPieces()
{
// the Piece CTOR automatically sets CapturedBy, etc. to initial starting values
// Note that pieces have fixed locs in the array: you do know exactly where each piece is.
// Useful for searching for the matching R,N on a side, to resolve naming ambiguities.
// DON'T MOVE THEM AROUND.  CODE NOW EXISTS THAT REFERS TO THEIR ABSOLUTE LOCATIONS.

Pieces[BLACK][0]  = Piece(BLACK,'Q', 0, 0,TPoint(0,0)); 	// dummy placeholder
Pieces[WHITE][0]  = Piece(WHITE,'Q', 0, 0,TPoint(0,0));		// dummy placeholder

Pieces[BLACK][1]  = Piece(BLACK,'P', 1, 0,TPoint(0,6));  	// QRP
Pieces[BLACK][2]  = Piece(BLACK,'P', 2, 0,TPoint(1,6));
Pieces[BLACK][3]  = Piece(BLACK,'P', 3, 0,TPoint(2,6));
Pieces[BLACK][4]  = Piece(BLACK,'P', 4, 0,TPoint(3,6));
Pieces[BLACK][5]  = Piece(BLACK,'P', 5, 0,TPoint(4,6));
Pieces[BLACK][6]  = Piece(BLACK,'P', 6, 0,TPoint(5,6));
Pieces[BLACK][7]  = Piece(BLACK,'P', 7, 0,TPoint(6,6));
Pieces[BLACK][8]  = Piece(BLACK,'P', 8, 0,TPoint(7,6)); 	// KRP
Pieces[BLACK][9]  = Piece(BLACK,'R', 9,16,TPoint(0,7)); 	// QR
Pieces[BLACK][10] = Piece(BLACK,'N',10,15,TPoint(1,7));
Pieces[BLACK][11] = Piece(BLACK,'B',11,14,TPoint(2,7));
Pieces[BLACK][12] = Piece(BLACK,'Q',12, 0,TPoint(3,7));
Pieces[BLACK][13] = Piece(BLACK,'K',13, 0,TPoint(4,7));
Pieces[BLACK][14] = Piece(BLACK,'B',14,11,TPoint(5,7));
Pieces[BLACK][15] = Piece(BLACK,'N',15,10,TPoint(6,7));
Pieces[BLACK][16] = Piece(BLACK,'R',16, 9,TPoint(7,7));

Pieces[WHITE][1]  = Piece(WHITE,'P', 1, 0,TPoint(0,1));	// QRP
Pieces[WHITE][2]  = Piece(WHITE,'P', 2, 0,TPoint(1,1));
Pieces[WHITE][3]  = Piece(WHITE,'P', 3, 0,TPoint(2,1));
Pieces[WHITE][4]  = Piece(WHITE,'P', 4, 0,TPoint(3,1));
Pieces[WHITE][5]  = Piece(WHITE,'P', 5, 0,TPoint(4,1));
Pieces[WHITE][6]  = Piece(WHITE,'P', 6, 0,TPoint(5,1));
Pieces[WHITE][7]  = Piece(WHITE,'P', 7, 0,TPoint(6,1));
Pieces[WHITE][8]  = Piece(WHITE,'P', 8, 0,TPoint(7,1));	// KRP
Pieces[WHITE][9]  = Piece(WHITE,'R', 9,16,TPoint(0,0));	// QR
Pieces[WHITE][10] = Piece(WHITE,'N',10,15,TPoint(1,0));
Pieces[WHITE][11] = Piece(WHITE,'B',11,14,TPoint(2,0));
Pieces[WHITE][12] = Piece(WHITE,'Q',12, 0,TPoint(3,0));
Pieces[WHITE][13] = Piece(WHITE,'K',13, 0,TPoint(4,0));
Pieces[WHITE][14] = Piece(WHITE,'B',14,11,TPoint(5,0));
Pieces[WHITE][15] = Piece(WHITE,'N',15,10,TPoint(6,0));
Pieces[WHITE][16] = Piece(WHITE,'R',16, 9,TPoint(7,0));	// KR

SideToMove = WHITE;
MoveIndex = 0;					// MUST be at 0, with pieces at initial squares
PieceScore[BLACK] = PieceScore[WHITE] = 41;

}								//ResetPieces
//----------------------------------------------------------------------------
// reset everything to startup state: reposition pieces, flush arrays
void ChessBoard::Reset()
{
ResetPieces();
Game.Flush();
Moves.Flush();
}             					//Reset
//-----------------------------------------------------------------------
// returns 1 if the given playerside can castle to the given KQside,
// else NEGATIVE, with reason encoded in return value.
// -1	King has moved
// -2   Rook has moved
// -3	King is in check
// -4	Intervening square(s) occupied
// -5	King must cross threatened square
// you should be able to call this for
// 1) a requested user move (to test legality), OR
// 2) by FindKingMoves() when identifying legal potential moves
int ChessBoard::CanCastle(int side, int KQside)	// KQside = QUEENSIDE/KINGSIDE
{
int QR = 9, K = 13, KR = 16;    // Pieces[] indexes of involved pieces
int from, to;					// to test between for intervening pieces

if(Pieces[side][K].MoveCount())			// King has moved
	return(-1);
if(KQside == QUEENSIDE)
{
	if(Pieces[side][QR].MoveCount())	// QR has moved
		return(-2);
	from = 1;         					// starting x-axis point to test
	to = 4;
}
else
{
	if(Pieces[side][KR].MoveCount())	// KR has moved
		return(-2);
	from = 5;
	to = 7;
}

// if(King in check)
// 	return(-3);

// are intervening squares occupied?
for(int x = from ; x < to ; x++)
	if(Occupant(TPoint(x,Pieces[side][K].Loc.y)))	// y for all is same as King's current
		return(-4);

// if(king must cross threatened square)
// return(-5);

return(1);
}                 				//CanCastle
//-----------------------------------------------------------------------
// returns pointer to piece occupying a square, or 0 if none.
Piece* ChessBoard::Occupant(const TPoint& p)
{
for(int side = BLACK ; side <= WHITE ; side++)
	for(int i = 1 ; i < 17 ; i++)
		if((Pieces[side][i].Loc == p) && !Pieces[side][i].CapturedBy)
			return(&Pieces[side][i]);
return(0);
}                  		//Occupant
//----------------------------------------------------------------------------
// determines whether a piece can land on a given square:
// it can if the square is on the board and is not occupied by a same-side piece.
// returns 	the point value of the piece that will be captured by the move (0 if none),
// 			or ERR if the move is illegal.
// Note that this only determines if a move is physically possible, and MUST be
// restricted to this use because the FindMoves loops quit as soon as this returns ERR.
// There are other conditions that can make a move illegal, which you must test elsewhere.
int ChessBoard::IsLegal(Piece* who, TPoint point)
{
if(!IsOnBoard(point))			// point isn't on the board
	return(ERR);
Piece* occ = Occupant(point);
if(occ == 0)   	       			// no occupant, move is OK, no value
	return(0);
if(occ->Side == who->Side) 		// same-side occupant, illegal
	return(ERR);
return(abs(occ->Value)); 		// opposing piece will be captured
}                     			// IsLegal(Piece)
//----------------------------------------------------------------------------
// determines whether a proposed Move is legal:
// ISLEGAL(PIECE*) (and maybe even CanCastle) PROBABLY CAN BE MERGED INTO THIS
//
// remember that the legality tests can receive program-generated moves
// (which will always be legal by some measures), OR user-requested moves
// (which may be illegal in ways program moves never are).
//
// returns 	1 if the move is legal, or a coded value < 0 if the move is illegal.
// 	RESERVED, FROM CANCASTLE:
// -1	King has moved
// -2   Rook has moved
// -3	King is in check
// -4	Intervening square(s) occupied
// -5	King must cross threatened square
// 	NEW VALUES:
// -6	It's the other side's turn
// -7   You can't capture your own piece
// -8   You can't capture a King
// -9
// -10
// -11
// -12
// -13
// -14
// NOTE: Move& m is NOT const, and IS changed, and the changes ARE USED by the caller.
// That is, this function helps build the Move that, if legal, gets added to Game[].
int ChessBoard::IsLegal(Move& m)
{
Piece* Who = &(Pieces[m.Side][m.Who]);
if(Who->Side != SideToMove)
	return(-6);

Piece* Captured = (m.Captured ? &(Pieces[m.CapSide][m.Captured]) : 0);
if(Captured)												// test for legal capture
{
	if(Who->Side == Captured->Side)                         // same-side capture
	{
		if((Who->Type == 'K') && (Captured->Type == 'R'))	// it's a castle request
		{
			m.CastleType = ((Captured->Loc.x < Who->Loc.x) ? QUEENSIDE : KINGSIDE);
			BOOL cancastle = CanCastle(Who->Side, m.CastleType);
			if(cancastle < 0)
				return(cancastle);							// see error codes above
		}
		else 												// same-side capture
			return(-7);
	}
	else 						// it's an opposide-side capture, but is it legal?
	{
		if(Captured->Type == 'K')	// THIS COULD BE THE TEST FOR END-OF-GAME
			return(-8);
	}
}			// end if(m.Captured)
else
{
	// test for e.p. capture: there's NO occupant, but there IS a capture,
	// which the raw move entered by user doesn't yet indicate.
}
// #error is King moving into check?

return(1);
}                     		// IsLegal(Move)
//----------------------------------------------------------------------------
// find all the moves the piece can make if it is a king
// legal moves are accumulated in Moves[].
void ChessBoard::FindKingMoves(Piece* p)
{
if(!p)
	return;
// just a fancy way of testing the 8 neighboring points
for(int i = -1 ; i < 2 ; i++)          	// rows
{
	int y = p->Loc.y + i;
	for(int j = -1 ; j < 2 ; j++)   	// columns
	{
		int x = p->Loc.x + j;
		TPoint newloc(x,y);
		if(newloc == p->Loc)			// don't test where the king already is
			continue;
		if(IsLegal(p,newloc) != ERR)	// #error you need to test for multiple values
		{                               // and only Add accordingly
			p->Moves.Add(newloc);		// reminder: kings can't be adjacent
			p->Guards.Add(newloc);  	// and it can guard a sq it can't move to
			Moves.Add(Move(p, p->Loc, newloc, Occupant(newloc)));
		}
	}
}
// special cases: a legal move that is legal for neither piece
// probably leave as-is: Add directly to Moves[], but not to either piece
if(CanCastle(p->Side, QUEENSIDE) > 0)
{
	Piece* rook = &Pieces[p->Side][9];
	Moves.Add(Move(p, p->Loc, rook->Loc, rook, QUEENSIDE));
}
if(CanCastle(p->Side, KINGSIDE) > 0)
{
	Piece* rook = &Pieces[p->Side][16];
	Moves.Add(Move(p, p->Loc, rook->Loc, rook, KINGSIDE));
}
}                 			//FindKingMoves
//----------------------------------------------------------------------------
// find all the moves the piece can make if it is a Rook
void ChessBoard::FindRookMoves(Piece* p)
{
if(!p)
	return;
// these are the increment amounts for moving each direction: left, right, up, down
static TSize s[4] = { TSize(-1,0), TSize(1,0), TSize(0,1), TSize(0,-1) };
TPoint newloc;
for(int i = 0 ; i < 4 ; i++)
	for(newloc = p->Loc + s[i] ; IsLegal(p,newloc) != ERR ; newloc += s[i])
	{
		p->Moves.Add(newloc);
		p->Guards.Add(newloc);
		Moves.Add(Move(p, p->Loc, newloc, Occupant(newloc)));
	}
}                    		//FindRookMoves
//----------------------------------------------------------------------------
// find all the moves the piece can make if it is a Bishop
void ChessBoard::FindBishopMoves(Piece* p)
{
if(!p)
	return;
// the increment amounts for moving each direction: upleft, upright, downleft, downright
static TSize s[4] = { TSize(-1,1), TSize(1,1), TSize(-1,-1), TSize(1,-1) };
TPoint newloc;
for(int i = 0 ; i < 4 ; i++)
	for(newloc = p->Loc + s[i] ; IsLegal(p,newloc) != ERR ; newloc += s[i])
	{
		p->Moves.Add(newloc);
		p->Guards.Add(newloc);
		Moves.Add(Move(p, p->Loc, newloc, Occupant(newloc)));
	}
}							//FindBishopMoves
//----------------------------------------------------------------------------
// find all the moves the piece can make if it is a Knight
void ChessBoard::FindKnightMoves(Piece* p)
{
if(!p)
	return;
// these are the increment amounts for moving to each of its 8 possible squares:
static TSize s[8] = { TSize(-1,2), TSize(1,2),  TSize(1,-2),  TSize(-1,-2),
					  TSize(2,1),  TSize(2,-1), TSize(-2,-1), TSize(-2,1) };
TPoint newloc;
for(int i = 0 ; i < 8 ; i++)
	if(IsLegal(p, newloc = p->Loc + s[i]) != ERR)
	{
		p->Moves.Add(newloc);
		p->Guards.Add(newloc);
		Moves.Add(Move(p, p->Loc, newloc, Occupant(newloc)));
	}
}						//FindKnightMoves
//----------------------------------------------------------------------------
// find all the moves the piece can make if it is a Pawn
void ChessBoard::FindPawnMoves(Piece* p)
{
if(!p)
	return;
TPoint newloc;
Piece* occ;
						// variables that differ depending on which side the piece is on
int homerow;			// row the pawn starts at
int eprow;				// row at which an e.p. capture may be possible
int nexttolast;			// row just prior to queening
int stepdir;            // y axis increment that advances it by 1 step

if(p->Side == WHITE) { homerow = 1; eprow = 4; nexttolast = 6; stepdir = 1; }
else				 { homerow = 6; eprow = 3; nexttolast = 1; stepdir = -1; }

if((p->Loc.y == eprow) && Game.GetItemsInContainer())	// en passant capture possible?
{
	// for both neighboring squares:
	// if neighbor square is on board, AND there is an opposing pawn there,
	// AND it JUST moved there with a 2-square jump on the previous move...
	// you can move to the square it jumped over AND capture it
	// remember the captured pawn isn't ON the square being moved to.
	for(int i = -1 ; i <= 1 ; i += 2)
	{
		TPoint neighbor = p->Loc + TSize(i, 0);
		if(IsOnBoard(neighbor))
		{
			occ = Occupant(neighbor);	// used below to set Move.Captured
			if(occ && (occ->Type == 'P') && (occ->Side == p->OtherSide()))
				if(Game[Game.GetItemsInContainer() - 1] ==
					Move(occ, occ->Loc + TSize(0,-stepdir * 2), occ->Loc))
				{
					newloc = p->Loc + TSize(i, stepdir * 1);
					if(IsLegal(p,newloc) != ERR)
					{
						p->Moves.Add(newloc);		// a legal loc that WON'T be found below
						// p->Guards.Add(newloc);	// don't Add: it WILL be Added below
						// this is why you must create actual Moves during FindMoves():
						// now is when you know who will be captured
						Moves.Add(Move(p, p->Loc, newloc, occ));
					}
				}
		}
	}					// for()
}						// end e.p. test
if(p->Loc.y == homerow)									// home row: try 2 ahead
{
	newloc = p->Loc + TSize(0, stepdir * 2);
	if((IsLegal(p,newloc) != ERR) && !Occupant(newloc))	// ANY piece blocks a pawn
	{
		p->Moves.Add(newloc);							// it doesn't guard this square
		Moves.Add(Move(p,p->Loc,newloc,0));  			// never a capture here
	}
}
newloc = p->Loc + TSize(0,stepdir * 1); 				// any row: try 1 ahead
if((IsLegal(p,newloc) != ERR) && !Occupant(newloc))		// ANY piece blocks a pawn
{
// 	if(p->Loc.y == nexttolast)	// next to last row: queen will result
// 		capval += 100;			// a reminder to adjust for queening value, SOMEWHERE.
	p->Moves.Add(newloc);								// it doesn't guard this square
	Moves.Add(Move(p, p->Loc, newloc, 0));         		// never a capture here
}
// for both squares diagonally ahead by 1: if square is on board, AND there is
// an opposing piece there, you can move to the square and capture it.
for(int i = -1 ; i <= 1 ; i += 2)
	if(IsOnBoard(newloc = p->Loc + TSize(i, stepdir)))
	{
		p->Guards.Add(newloc);						// it definitely guards this square
		occ = Occupant(newloc);
		if(occ && (occ->Side == p->OtherSide()))	// IsLegal may already do otherside test
			if(IsLegal(p,newloc) != ERR)
			{
// 				if(p->Loc.y == nexttolast)	// next to last row: queen will result
// 					capval += 100;			// adjust for queening value, SOMEWHERE.
				p->Moves.Add(newloc);  		// in this case, it can also move to the square
				Moves.Add(Move(p, p->Loc, newloc, occ));
			}
	}
}          				//FindPawnMoves
//----------------------------------------------------------------------------
// find all possible legal moves, tabulate all stats
void ChessBoard::Tabulate()
{
for(int side = BLACK ; side <= WHITE ; side++)
	for(int i = 1 ; i < 17 ; i++)		// find all possible moves for all pieces
	{
		Piece* p = &(Pieces[side][i]);
		switch(p->Type)
		{
			case 'P': FindPawnMoves(p); break;
			case 'R': FindRookMoves(p); break;
			case 'N': FindKnightMoves(p); break;
			case 'B': FindBishopMoves(p); break;
			case 'K': FindKingMoves(p); break;
			case 'Q': FindRookMoves(p); FindBishopMoves(p); break;
		}
	}
// Stats to have available (always or on request)
// (all can be compiled from Sq[][], Pieces[][] and Moves[]):
// EACH PIECE:
// 	list of sqs it guards: Guards[]
// 	# of sqs it guards: Guards.GetItemsInContainer()
// 	list of same-side pieces guarding its sq
// 	list of opponent pieces threatening its sq
// EACH SQUARE (SEPARATE LISTS, BLACK/WHITE):
// 	# of pieces guarding it
// 	total value of pieces guarding it
// EACH SIDE:
//	total value of its pieces still on board: PieceScore[]
// 	# of squares guarded by all pieces
// 	list of pieces threatened
// 	# of pieces threatened
// 	total value of threatened pieces
// 	list of opponent pieces threatened
// 	# of opponent pieces threatened
// 	total value of opponent pieces threatened

}             					//Tabulate
//----------------------------------------------------------------------------
// do what is necessary to post a move to the game.
// CALLER MUST THEN HANDLE ANY DISPLAY-RELATED UPDATES
BOOL ChessBoard::PostMove(const Move& m, BOOL newmove)
{
// #error do error checking on m members?
Piece* Who = &Pieces[m.Side][m.Who];
if(m.CastleType)
{
	Piece* Captured = &Pieces[m.CapSide][m.Captured]; 	// the rook, not actually captured
	if(m.CastleType == QUEENSIDE)
	{
		Who->MoveTo(TPoint(2, Who->Loc.y));          	// the king
		Captured->MoveTo(TPoint(3, Captured->Loc.y));	// the rook
	}
	else
	{
		Who->MoveTo(TPoint(6, Who->Loc.y));
		Captured->MoveTo(TPoint(5, Captured->Loc.y));
	}
}
else
{
	if(m.Captured)							// handle capture, if any
	{
		// rely solely on already-set ->Captured, so e.p. capture gets done
		// i.e. don't recheck for occupant: there may not be one.
		// somewhere, you may have to deal specially with Squares (3 involved instead of 2)
		Piece* Captured = &Pieces[m.CapSide][m.Captured];
		Captured->CapturedBy = Who->Index;
		PieceScore[Captured->Side] -= Captured->Value;
	}
	Who->MoveTo(m.To);						// tell piece where it now sits
}
if(newmove)
{
	// a new move truncates Game[] back to that point: Destroy all existing moves beyond.
	while(Game.GetItemsInContainer() > MoveIndex)
		Game.Destroy(MoveIndex);
	Game.Add(m);                           		// log the move to Game[]
}
MoveIndex++;
SideToMove = Who->OtherSide();

// refigure ALL statistics:
// rebuild Moves[] array with all legal moves by all pieces
// tally other stats (separate function so you can do it without posting a move)

return(TRUE);
}                    			//PostMove
//----------------------------------------------------------------------------
// undo last move in Game[], restoring any captured piece
BOOL ChessBoard::UndoMove()
{
int N = MoveIndex; 		// N - 1 is the actual index of the move to undo
if(N <= 0)				// so if N==0, you're at the game start
	return(FALSE);

const Move& m(Game[N - 1]);				// a short-named copy
Piece* Who = &(Pieces[m.Side][m.Who]);
Piece* Captured = (m.Captured ? &(Pieces[m.CapSide][m.Captured]) : 0);
if(Captured)
	if(m.CastleType)                  	// if a castling move,
		Captured->UnMove();				// then put the Rook back, too.
	else  								// a real Captured piece doesn't have to UnMove
	{
		Captured->CapturedBy = 0;					// restore captured piece
		PieceScore[m.CapSide] += Captured->Value;  	// and add back its Value
	}
Who->UnMove();                      	// Mover always UnMoves
SideToMove = m.Side;                    // it's the side just UnMoved's turn
// Game.Destroy(N - 1);  				// delete the move from the game
MoveIndex--;                            // just move the pointer backwards in Game
return(TRUE);
}								//UndoMove
//----------------------------------------------------------------------------
// Redo last Undone move in Game[], by actually replaying it.
BOOL ChessBoard::RedoMove()
{
int N = MoveIndex; 					// N is the actual index of the move to redo
if(N >= Game.GetItemsInContainer())	// so if N==GetItems, there are none to redo
	return(FALSE);

PostMove(Game[N], FALSE);	// replay the move, but don't re-add it (it's not new)
return(TRUE);
}								//RedoMove
//----------------------------------------------------------------------------
// 						end class ChessBoard
//////////////////////////////////////////////////////////////////////////////
class Player
{
public:
	Player(int side);
	~Player();

	int Side;

protected:
};
//----------------------------------------------------------------------------
Player::Player(int side) : Side(side)
{
}                 	//constructor
//----------------------------------------------------------------------------
Player::~Player()
{
}
//----------------------------------------------------------------------------
// 						end class Player
//////////////////////////////////////////////////////////////////////////////
// a physical square representing a logical square on the chess board, for displaying.
class Square : public TWindow
{
public:
	Square(TWindow* parent, const char* title, TPoint loc);

	// functions
	static void Deselect();

	// overridden virtuals
	void Paint(TDC&, BOOL, TRect&);

	// event handlers
	void EvSize(UINT sizetype, TSize& size);
	void EvLButtonDown(UINT modkeys, TPoint& point);
// not used
// 	void EvMouseMove(UINT modkeys, TPoint& point);
// 	void EvLButtonUp(UINT modkeys, TPoint& point);
// 	void EvRButtonDown(UINT modkeys, TPoint& point);

	// variables
	TPoint SquareLoc;	// so it knows its own location in the physical screen grid.
	TPoint BoardLoc;	// SquareLoc + TSize(-1,-1): the ChessBoard square it represents
	Piece* piece;		// pointer to the occupant, if any
	BOOL IsBorder;		// whether it's on the border
	BOOL IsLabel;		// on border AND is a label
						// 3 often-used output aliases for the BoardLoc this square represents
	char AbsName[3];	// absolute notation: A1-H8, etc.
	char WhiteName[4];	// relative to WHITE side: QP1, etc.
	char BlackName[4];	// relative to BLACK side: QP1, etc.

	// static variables
	// This is a central loc for these, where anyone can find them, but if there are
	// ever multiple games in process at once, Selected could cause pieces to jump
	// between games, and other similar problems!
	// the Square class has no knowledge of SChessWindow OR its member ChessBoard,
	// and shouldn't, especially since a "picked up" piece is of no interest to
	// the Board until it's moved.
	static Square* Selected;	// the square containing piece selected for a move
	static Move* Pending;		// a Move requested by user, using the mouse, or 0

protected:

DECLARE_RESPONSE_TABLE(Square);
};
//----------------------------------------------------------------------------
DEFINE_RESPONSE_TABLE1(Square, TWindow)
	EV_WM_SIZE,
	EV_WM_LBUTTONDOWN,
// 	EV_WM_LBUTTONUP,
// 	EV_WM_RBUTTONDOWN,
// 	EV_WM_MOUSEMOVE,
END_RESPONSE_TABLE;
//----------------------------------------------------------------------------
// initialize static members
Square* Square::Selected = 0;	// when nonzero, it's just a pointer to the selected square
Move* Square::Pending = 0;		// when nonzero, it's a newed Move that must be deleted
//----------------------------------------------------------------------------
// constructor
Square::Square(TWindow* parent, const char* title, TPoint loc) : TWindow(parent, title)
{
Attr.Style |= (WS_BORDER | WS_CHILD | WS_VISIBLE);
Attr.Style &= ~(WS_CAPTION);

piece = 0;
IsBorder = IsLabel = FALSE;
SquareLoc = loc;
BoardLoc = SquareLoc + TSize(-1,-1);			// will be used often, so precalc.
AbsName[0] = WhiteName[0] = BlackName[0] = 0;
AbsName[0] = (char)('A' + BoardLoc.x); AbsName[1] = (char)('1' + BoardLoc.y); AbsName[2] = 0;

// #error pull out to global?
static const char* f[8] = { "QR", "QN", "QB", "Q", "K", "KB", "KN", "KR" };
TColor bkcolor;									// square background color (light or dark)
if(ChessBoard::IsOnBoard(BoardLoc))
{
	bkcolor = ((((SquareLoc.x + SquareLoc.y) % 2) == 0) ? Colors[0] : Colors[1]);

	// set up the square's names in the various formats
	ostrstream(WhiteName,sizeof(WhiteName)) << f[BoardLoc.x] << SquareLoc.y << ends;
	ostrstream(BlackName,sizeof(BlackName)) << f[BoardLoc.x] << (9 - SquareLoc.y) << ends;

// 	ofstream("d:\\temp\\temp.txt",ios::app) << SquareLoc << " = " << BoardLoc << " = " <<
// 		AbsName << " = " << WhiteName << " = " << BlackName << endl;

}
else
{
	IsBorder = TRUE;
	bkcolor = Colors[6];

	// border squares store their labels in their caption text.
	// but no HWindow here, so it only sets Title, not the interface element caption
	if((SquareLoc.x > 0) && (SquareLoc.x < 9)) 	// it's a column (file) label
	{
		SetCaption(string(AbsName[0]).c_str());
		IsLabel = TRUE;
		// file label squares A-H also get the names of their files ("QR", etc.),
		// without the digit, for easy lookup when building piece names for output.
		// SquareLocs are 1-8 here, so BoardLoc use is correct, as 0-7 indexes.
		ostrstream(WhiteName,sizeof(WhiteName)) << f[BoardLoc.x] << ends;
		ostrstream(BlackName,sizeof(BlackName)) << f[BoardLoc.x] << ends;
	}
	if((SquareLoc.y > 0) && (SquareLoc.y < 9))	// it's a row (rank) label
	{
		SetCaption(string(AbsName[1]).c_str());
		IsLabel = TRUE;
	}
}
SetBkgndColor(bkcolor);

}                				//constructor
//----------------------------------------------------------------------------
void Square::EvSize(UINT sizeType, TSize& size)
{
// may be unnecessary (or wrong method), but I thought I once found a WM_SIZE message
// early IN the series of Windows messages that created a new window.
// foolproof would be use my own BOOL iscreated, set true in setupwindow.
if(IsWindow())
	Invalidate(TRUE);				// force repaint in new size (IS necessary)
TWindow::EvSize(sizeType,size);
}                 				//EvSize
//----------------------------------------------------------------------------
// pick up or put down a piece to move it.
// for now, don't change cursor: unnecesssary complication and possible errors.
// also don't use drag method, to avoid wshowfs-type hangup problems because
// we'd be dragging across different windows.  Also, for either, SChessWindow
// would have to keep the cursor changed, AND cancel any piece selection if the
// drag went outside its client rect.
void Square::EvLButtonDown(UINT modkeys, TPoint& point)
{
TWindow::EvLButtonDown(modkeys,point);
if(Pending)						// only 1 move at a time
	return;
Invalidate(TRUE);				// necessary for either case below
if(!Selected)					// if no square is currently selected
{
	if(!piece || IsBorder)		// no occupant here to move
		return;
	Selected = this;			// mark it as about to be moved
	return;
}
// if it falls through, it's a move request, and this square is its new loc.
Square* selected = Selected;		// copy for local use
Selected = 0;						// and zero the static immediately.
selected->Invalidate(TRUE);			// force the "from" square to redraw
if(IsBorder || (selected == this))	// you can't move to the same square (pgm crashes)
{
	MessageBeep(MB_ICONEXCLAMATION);
	return;
}
Piece* Who = selected->piece;   				// always nonzero
Piece* Captured = piece;						// can be 0

// create Pending Move, for SChessWindow::IdleAction to detect and process
Pending = new Move(Who,Who->Loc,BoardLoc,Captured);

}                        	//EvLButtonDown
//----------------------------------------------------------------------------
void Square::Paint(TDC& pdc, BOOL erase, TRect& invalidarea) // OWLPG:37
{
TWindow::Paint(pdc,erase,invalidarea);
if(IsIconic() || (!piece && !IsBorder))		// keep piece and border test!
	return;

TRect clientrect = GetClientRect();
int weight = FW_NORMAL;               		// defaults here are for the corner squares
int height = clientrect.Height() / 2;
TColor color = Colors[12];
string text = Title;						// all border squares draw their Titles
if(IsBorder)
{
	if(IsLabel)								// override for a grid label
	{
		height = clientrect.Height() / 2;
		weight = FW_BOLD;
		color = (((SquareLoc.x == 0) || (SquareLoc.x == 9)) ? Colors[7] : Colors[8]);
	}
}
else		// (this ASSUMES square contains a piece): draw it
{
	height = clientrect.Height() * 3 / 4;
	weight = FW_BOLD;      								//FW_NORMAL;
	text = string((char)piece->Type);
	if(Selected == this) color = ((piece->Side == BLACK) ? Colors[4] : Colors[5]);
	else                 color = ((piece->Side == BLACK) ? Colors[2] : Colors[3]);
}
// later create my own font in .RC with my piece images where the letters should be.
// but will they scale properly?
// experiment with some other fonts, too.
TFont font("Arial",height,0,0,0,weight);	// -h makes it a little bigger
pdc.SelectObject(font);
pdc.SetBkMode(TRANSPARENT); 		// so background color shows through
pdc.SetTextColor(color);
pdc.DrawText(text.c_str(), -1, clientrect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
pdc.RestoreFont();
}                  				//Paint
//----------------------------------------------------------------------------
void Square::Deselect()
{
if(Selected)
{
	Selected->Invalidate(TRUE); // force redraw of the piece, in deselected color
	Selected = 0;				// cancel any selection
}
}								//Deselect
//----------------------------------------------------------------------------
// 							end class Square
//////////////////////////////////////////////////////////////////////////////
// A window in which a game of Chess takes place
class SChessWindow : public TLayoutWindow
{
public:
	SChessWindow(TWindow* parent = 0, const char* title = 0);
	~SChessWindow();

	friend ostream& operator << (ostream&, const SChessWindow&);  // put-to operator

	BOOL IdleAction(long idlecount);

protected:
	// pointers
	ChessBoard Board;			// logical board where the game takes place
	Square* Squares[10][10];	// TWindow-derived physical display squares
								// note that Square storage is nonstandard Squares[x][y],
								// for intuitive access notation.
	// other variables
	FileArray FileList;			// command line arguments and/or user-selected files
	int BoardView;				// which side is at bottom of screen
	BOOL Animating;				// whether auto-animation is in process

	// for global alarm function; for timing a game's moves, you must give one to each SIDE.
	Stopwatch sw;

	// normal functions
	void SetSquareMetrics(int pointofview = WHITE);		// BLACK or WHITE
	void UpdateAllSquares(BOOL invalidate);		// empty, place all occupants, Invalidate all
	void UpdateCornerSquares(BOOL invalidate);	// update the 4 corner status data squares
	string FullPieceName(const Piece* p, BOOL checkboard = FALSE);	// fully qualified

	// overridden virtuals
	void EvSize(UINT sizetype, TSize& size);
	void EvTimer(UINT timerid);
	BOOL CanClose();
	void Paint(TDC&, BOOL, TRect&);

	// event handlers
	void EvRButtonDown(UINT, TPoint&);
	void CmFileSaveAs();				// save current game's log to a file
	void CmFileExit() { GetApplication()->GetMainWindow()->CloseWindow(); }
	void CmFileNew();    				// start a new game with pieces at initial locs
	void CmFileOpen();                  // load a saved game log
	void CmReverseBoard();
	void CmGameReset();					// reset to starting pos, but NOT a new game.
	void CmGameStepBack();
	void CmGameStepForward();
	void CmGetUserMove(); 				// via a TInputDialog
	void CmUndoMove();
	void CmRedoMove();
	void CmGameAnimate();
	void CmHelpIndex();
	void CmHelpAbout();
	void CmTimerSet();
	void CmTimerReset();

DECLARE_RESPONSE_TABLE(SChessWindow);
};
DEFINE_RESPONSE_TABLE1(SChessWindow, TLayoutWindow)
	EV_WM_SIZE,
	EV_WM_TIMER,
	EV_WM_RBUTTONDOWN,
	EV_COMMAND(CM_FILEEXIT,CmFileExit),
	EV_COMMAND(CM_FILENEW,CmFileNew),
	EV_COMMAND(CM_FILEOPEN,CmFileOpen),
	EV_COMMAND(CM_FILESAVEAS,CmFileSaveAs),
	EV_COMMAND(CM_GAMERESET,CmGameReset),
	EV_COMMAND(CM_GAMESTEPBACK,CmGameStepBack),
	EV_COMMAND(CM_GAMESTEPFORWARD,CmGameStepForward),
	EV_COMMAND(CM_GETUSERMOVE,CmGetUserMove),
	EV_COMMAND(CM_GAMEUNDOMOVE,CmUndoMove),
	EV_COMMAND(CM_GAMEREDOMOVE,CmRedoMove),
	EV_COMMAND(CM_GAMEANIMATE,CmGameAnimate),
	EV_COMMAND(CM_OPTIONSREVERSEBOARD,CmReverseBoard),
	EV_COMMAND(CM_HELPINDEX,CmHelpIndex),
	EV_COMMAND(CM_HELPABOUT,CmHelpAbout),
	EV_COMMAND(CM_TIMERRESET,CmTimerReset),
	EV_COMMAND(CM_TIMERSET,CmTimerSet),
END_RESPONSE_TABLE;
//----------------------------------------------------------------------------
// constructor
SChessWindow::SChessWindow(TWindow* parent, const char* title) : TLayoutWindow(parent, title)
{
//-----------------------------------------
// Set up this window's startup attributes:
Attr.W = 600;
Attr.H = 450;
// If layout window has a border, then it will automatically adjust children by 1 pixel
Attr.Style |= (WS_BORDER);
// Attr.Style |= (WS_CLIPCHILDREN);	// prevents overwriting the child squares
SetBkgndColor(Colors[13]);
//-----------------------------------------
// initialize other variables
Animating = FALSE;

//-----------------------------------------
// initialize pointers
// set up the child window board squares
for(int x = 0 ; x < 10 ; x++)
	for(int y = 0 ; y < 10 ; y++)
		Squares[x][y] = new Square(this,0,TPoint(x,y));	// create the window
//-----------------------------------------
// set up child window layout metrics (now, AFTER ALL the child window pointers have values!)
SetSquareMetrics(BoardView = WHITE);
UpdateAllSquares(FALSE);					// put each piece in its square

//-----------------------------------------
// #error add the code to use the filename, and associate w/.CHS in FileManager
// see winbrot for how to use filelist
#if defined(__WIN32__)			// get command line arguments (list of files to display)
	string pgmname;				// discard program name, then the rest are file names
	istrstream(GetCommandLine()) >> pgmname >> FileList;
#else
	for(int i = 1 ; i < _argc ; i++)
		FileList.AddFile(string(_argv[i]));
#endif	//(__WIN32__)

// you probably can load the game here, but you can't do several things that
// Invalidate() or UpdateSquares for the display.

}						//constructor
//----------------------------------------------------------------------------
// destructor
SChessWindow::~SChessWindow()
{
if(Animating)       	// in case the app closes while animating
	KillTimer(1);
}                    	//destructor
//----------------------------------------------------------------------------
ostream& operator << (ostream& os, const SChessWindow&) { return(os); }
//----------------------------------------------------------------------------
// set layout metrics for the child windows, for the given point of view (BLACK or WHITE)
// (side views would be relatively simple, too)
// the pgm's board setup turns out pretty handy: each square has a permanent x,y identity,
// the pieces are fixed to them, and row/column names stay the same, so calculating
// moves remains the same; you just rearrange the squares, just like rotating the board.
// DO NOT Invalidate() or do other window ops here, because this fn is called by ctor.
void SChessWindow::SetSquareMetrics(int pointofview)
{
int x, y;
TLayoutMetrics lm;

// these are the same for all windows
lm.X.Units = lm.Y.Units = lm.Width.Units = lm.Height.Units = lmLayoutUnits;
lm.Width.PercentOf(lmParent, 10, lmHeight);   	// same reference dimension
lm.Height.PercentOf(lmParent, 10, lmHeight);	// for both, so they're square.
if(pointofview == WHITE)						// layout for WHITE
{
	// set up Square[0][0].  It is the only one set relative to lmParent.
	// must go from bottom up so screen squares correspond easily with Board locs.
	// i.e. ChessBoard square A1(0,0) must map to display Square(1,1)

	lm.X.Set(lmLeft, lmRightOf, lmParent, lmLeft, 0);
	lm.Y.Set(lmBottom, lmAbove, lmParent, lmBottom, 0);
	SetChildLayoutMetrics(*(Squares[0][0]), lm);

	// set X for the entire lefthand column of label squares
	lm.X.SameAs(Squares[0][0], lmLeft);
	for(y = 1 ; y < 10 ; y++)
	{
		// set each square's bottom above the top of the one below it
		lm.Y.Set(lmBottom, lmAbove, Squares[0][y - 1], lmTop, 0);
		SetChildLayoutMetrics(*(Squares[0][y]), lm);
	}
	// set Y for the entire bottom row of label squares
	lm.Y.SameAs(Squares[0][0], lmBottom);
	for(x = 1 ; x < 10 ; x++)
	{
		// set each square's left edge right of the right edge of the one to its left
		lm.X.Set(lmLeft, lmRightOf, Squares[x - 1][0], lmRight, 0);
		SetChildLayoutMetrics(*(Squares[x][0]), lm);
	}
	// all the rest of the squares can get their constraints from the border squares
	for(x = 1 ; x < 10 ; x++)
	{
		lm.X.SameAs(Squares[x][0], lmLeft);			// entire column has the same left edge
		for(y = 1 ; y < 10 ; y++)
		{
			lm.Y.SameAs(Squares[0][y], lmBottom);			// now set bottom of each
			SetChildLayoutMetrics(*(Squares[x][y]), lm);
		}
	}
}            					// layout for WHITE
else							// layout for BLACK
{
	// set up Square[9][9] (at bottom left).  It is the only one set relative to lmParent.
	lm.X.Set(lmLeft, lmRightOf, lmParent, lmLeft, 0);
	lm.Y.Set(lmBottom, lmAbove, lmParent, lmBottom, 0);
	SetChildLayoutMetrics(*(Squares[9][9]), lm);

	// set X for the entire lefthand column of label squares
	lm.X.SameAs(Squares[9][9], lmLeft);
	for(y = 8 ; y >= 0 ; y--)
	{
		// set each square's bottom above the top of the one below it
		lm.Y.Set(lmBottom, lmAbove, Squares[9][y + 1], lmTop, 0);
		SetChildLayoutMetrics(*(Squares[9][y]), lm);
	}
	// set Y for the entire bottom row of label squares
	lm.Y.SameAs(Squares[9][9], lmBottom);
	for(x = 8 ; x >= 0 ; x--)
	{
		// set each square's left edge right of the right edge of the one to its left
		lm.X.Set(lmLeft, lmRightOf, Squares[x + 1][9], lmRight, 0);
		SetChildLayoutMetrics(*(Squares[x][9]), lm);
	}
	// all the rest of the squares can get their constraints from the border squares
	for(x = 8 ; x >= 0 ; x--)
	{
		lm.X.SameAs(Squares[x][9], lmLeft);			// entire column has the same left edge
		for(y = 8 ; y >= 0 ; y--)
		{
			lm.Y.SameAs(Squares[9][y], lmBottom);	// now set bottom of each
			SetChildLayoutMetrics(*(Squares[x][y]), lm);
		}
	}
}                         		// layout for BLACK
}                    			//SetSquareMetrics
//----------------------------------------------------------------------------
void SChessWindow::CmReverseBoard()
{
if(Animating)					// stop auto-animate (unsure if necessary here)
	CmGameAnimate();
HCURSOR oldcursor = ::SetCursor(::LoadCursor(NULL,IDC_WAIT));
BoardView = Piece::OtherSide(BoardView);
SetSquareMetrics(BoardView);   	// set metrics to match the new viewpoint
UpdateCornerSquares(FALSE);		// nec. because the data squares cross over the board
Layout();						// implement the new metrics (IT will Invalidate the sqs)
::SetCursor(oldcursor);  		// restore cursor
}               				//CmReverseBoard
//----------------------------------------------------------------------------
// reset squares en masse, usually to reflect a Reset or greatly-changed ChessBoard.
// set invalidate FALSE when calling from ctor.
void SChessWindow::UpdateAllSquares(BOOL invalidate)
{
Square::Deselect();
for(int x = 0 ; x < 10 ; x++)		// iterate through ALL squares
	for(int y = 0 ; y < 10 ; y++)
	{
		Square* s = Squares[x][y];
		Piece* occupant = Board.Occupant(s->BoardLoc);
		if(s->piece != occupant)  	// mark for redrawing only squares with changes
		{                           // any other changes to test for?
			s->piece = occupant;
			if(invalidate)
				s->Invalidate(TRUE);
		}
	}
UpdateCornerSquares(invalidate);
// if(invalidate)		// no point: there's nothing IN the main window to redraw
// 	Invalidate(TRUE);   // causes screen blanking (undesirable unless absolutely necessary)
}               				//UpdateAllSquares
//----------------------------------------------------------------------------
// reset ALL the corner squares, then redraw specific ones with their revised info.
// Much easier than keeping track of which is which.  When you rotate the board, you
// must use diff squares because the old ones cross the board.
// the green squares could also contain time info or a timer.
void SChessWindow::UpdateCornerSquares(BOOL invalidate)
{
Square* s[4] = { Squares[0][0], Squares[0][9], Squares[9][0], Squares[9][9] };
for(int i = 0 ; i < 4 ; i++)
{
	s[i]->SetBkgndColor(Colors[11]);
	s[i]->SetCaption(0);
	if(invalidate)
		s[i]->Invalidate(TRUE);
}
Square *wgo, *bgo, *ws, *bs; 	// the green "GO" squares and the piece-score squares
if(BoardView == WHITE) { wgo = s[0]; bgo = s[1]; ws = s[2]; bs = s[3]; }
else				   { wgo = s[2]; bgo = s[3]; ws = s[0]; bs = s[1]; }

if(Board.SideToMove == WHITE) wgo->SetBkgndColor(Colors[9]);
else						  bgo->SetBkgndColor(Colors[9]);

ws->SetCaption(tostring(Board.PieceScore[WHITE]).c_str());
bs->SetCaption(tostring(Board.PieceScore[BLACK]).c_str());
}               			//UpdateCornerSquares
//----------------------------------------------------------------------------
// #error test possibility of constraining aspect ratio of the window, to keep board square:
// set Attr.W to a percentage of Attr.H
// Layout() is much faster if the window height hasn't changed, which means that
// it is Layout's many calculations that are slow, not the window redrawing.
void SChessWindow::EvSize(UINT sizeType, TSize& size)
{
if(IsWindow())							// Invalidate is necessary here:
	Invalidate(TRUE);					// eliminates garbage while "undrawing"
TLayoutWindow::EvSize(sizeType,size);	// Layout() forces all the squares to repaint
}                        		//EvSize
//----------------------------------------------------------------------------
// receiving timerid 1 is the signal to auto-animate the next move.
void SChessWindow::EvTimer(UINT timerid)
{
if(timerid != 1)
	return;
if(!Board.RedoMove())							// if we reached the end of the game,
{
// 	clock_t start = clock();
// 	while((clock() - start) < (CLK_TCK / 2));	// do nothing for 1/2 second
												// but will WM_TIMER msgs pile up?
	MessageBeep(-1);							// audible indicator of game start
	Board.ResetPieces();						// reset to beginning of game
}
UpdateAllSquares(TRUE);			// put any moved pieces on their proper Squares
}								//EvTimer
//----------------------------------------------------------------------------
void SChessWindow::EvRButtonDown(UINT modKeys, TPoint& point)
{
TLayoutWindow::EvRButtonDown(modKeys,point);			// probably does nothing
}								//EvRButtonDown
//----------------------------------------------------------------------------
// wherever this fn winds up, here is how to construct a move notation that is as
// abbreviated as possible without being ambiguous.
string SChessWindow::FullPieceName(const Piece* p, BOOL checkboard)
{
// not used.
// keep for possible use to create names DURING game time.
string s;
return(s);
}      							//FullPieceName
//----------------------------------------------------------------------------
// write the Game[] log to a file in several formats, each in its own block in the file.
void SChessWindow::CmFileSaveAs()
{
// static so that the name of the last-saved file is the default
// #error: note this would be shared by all class objects if you allow multiples
static TOpenSaveDialog::TData filedata(							// flags ok
	OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,
	"Game Move Listings|*.CHS|Board Positions|*.CHB|All Files|*.*|",0,0,"CHS");
if(TFileSaveDialog(this,filedata).Execute() != IDOK)	// select file
	return;
ofstream os(filedata.FileName);

//--------------------------
// .CHB FORMAT: write 1 board position only:
string s(filedata.FileName);
s.to_upper();
if(s.contains(".CHB"))
{                   			// this is the real code for this block
	os << (string)Board << endl;
	return;
}
// if this is enabled (instead of above), it writes CHESS.BMP (screen) instead of the board
// it doesn't work, and is more trouble than it's worth.
// after it works, make it a screen-capture function
#if 0
{
	// for all I know, this might work, but in this app nothing is IN the client window!
	// it's all in child windows.  I tried TScreenDC: no.
	TClientDC clientdc(*this);	// create a TDC for the client area of this window
	TRect r = GetClientRect();  // get size of client area
	TDib dib(r.Width(),r.Height(), 256, DIB_RGB_COLORS); // create matching TDib, 256 colors
										// copy the bits from the screen DC to the dib
	TDibDC(dib).BitBlt(r, clientdc, TPoint(0,0), SRCCOPY);
	dib.WriteFile("d:\\temp\\chess.bmp");		// write the dib to a file
	return;
}
#endif 	// 0

//--------------------------
// 1. (TPoint)(TPoint)format	THE PROGRAM READS THIS FORMAT
os << Board << endl;
os << endl;

// the remaining 2 formats require calculations using Square members
//--------------------------
// 2. E2-E4  PxP format		text, for user only
for(int i = 0 ; i < Board.Game.GetItemsInContainer() ; i++)
{
	int mover = (i % 2);
	const Move& m = Board.Game[i];
	Piece* Who = &(Board.Pieces[m.Side][m.Who]);
	TPoint from = m.From + TSize(1,1);
	TPoint to = m.To + TSize(1,1);
	os << (mover ? '-' : '*') << setw(3) << ((i / 2) + 1) << ". ";
	if(m.CastleType)
		os << ((m.CastleType == QUEENSIDE) ? "O-O-O" : "O-O");
	else
	{
		os << Squares[from.x][from.y]->AbsName << "-" << Squares[to.x][to.y]->AbsName;
		if(m.Captured)
			os << " " << (char)Who->Type << "x"
			   << (char)(Board.Pieces[m.CapSide][m.Captured].Type);
	}
	os << endl;
}
os << endl;
//--------------------------
// 3. KP-KP4  PxP format			text, for user only
for(i = 0 ; i < Board.Game.GetItemsInContainer() ; i++)
{
	int mover = (i % 2);
	const Move& m = Board.Game[i];
	Piece* Who = &(Board.Pieces[m.Side][m.Who]);
	TPoint from = m.From + TSize(1,1);
	TPoint to = m.To + TSize(1,1);

	// NUMBERING SEQUENTIALLY AS (I + 1) ALSO WORKS WELL.  CAN'T DECIDE WHICH.
	os << (mover ? '-' : '*') << setw(3) << (i + 1) << ". ";       // move number
// 	os << (mover ? '-' : '*') << setw(3) << ((i / 2) + 1) << ". "; // move number

if(m.CastleType)
		os << ((m.CastleType == QUEENSIDE) ? "O-O-O" : "O-O");
	else
	{
		// KEEP THIS FORMAT FOR THIS LISTING(3). MAKE ANY IMPROVEMENT ATTEMPTS IN #4.
		char t = (char)Who->Type;

		// KQB always use just 1 char: never ambiguous
		// #error there can be > 1 Queen, so it can be ambiguous, too.
		// RN require fully-qualified name, P requires it only if move is a capture
		if(strchr("RN",t) || ((t == 'P') && m.Captured))
			os << Squares[from.x][0]->WhiteName;// prefix copied from column labels (b/w same)
		os << string(t) << "-";                 // then the actual piece char

		// always using fully-qualified square name eliminates MANY possible ambiguities
		os << (mover ? Squares[to.x][to.y]->BlackName : Squares[to.x][to.y]->WhiteName);

		// remaining potential ambiguities are few, but CAN'T be noticed or fixed here,
		// only at the time the move is posted.  At that time, you could also calc
		// abbreviated forms for some of the "always-fully-qualified" forms above, if no ambigs.

		if(m.Captured)
			os << " " << (char)Who->Type << "x"
			   << (char)(Board.Pieces[m.CapSide][m.Captured].Type);
	}
	os << endl;
}
os << endl;
//--------------------------
// 4. KP-KP4  PxP format, with W/B moves on same line, for easy spreadsheet pasting.
for(i = 0 ; i < Board.Game.GetItemsInContainer() ; i++)
{
	int mover = (i % 2);
	const Move& m = Board.Game[i];
	Piece* Who = &(Board.Pieces[m.Side][m.Who]);
	TPoint from = m.From + TSize(1,1);
	TPoint to = m.To + TSize(1,1);

// 	if(!(i % 2))         	// rem out this line to number both columns
		os << setw(3) << setiosflags(ios::right) << ((i / 2) + 1) << ". ";	// move number

	string s;			// build text in string, then output to file in fixed field-width
		 if(m.CastleType == QUEENSIDE)	s =  "O-O-O";
	else if(m.CastleType == KINGSIDE)	s =  "O-O";
	else
	{
		char t = (char)Who->Type;
		if(m.Captured)
		{
			// if it's a capture, use 2 fully-qualified PIECE names,
			// and use the result instead of the move
			if(strchr("RNP",t))
				s += Squares[from.x][0]->WhiteName;
			s += string(t) + "x";
			char u = (char)(Board.Pieces[m.CapSide][m.Captured].Type);
			if(strchr("RNPB",u))				// (almost)always qualify the captive
				s += Squares[to.x][0]->WhiteName;
			s += string(u);
		}
		else
		{
			if(strchr("RN",t) || ((t == 'P') && m.Captured))
				s += Squares[from.x][0]->WhiteName;
			s += string(t) + "-";
			s += (mover ? Squares[to.x][to.y]->BlackName : Squares[to.x][to.y]->WhiteName);
		}
	}
	os << setw(16) << setiosflags(ios::left) << s;
	if(i % 2)
		os << endl;
}
os << endl;
}                      		//CmFileSaveAs
//----------------------------------------------------------------------------
// start a new game
void SChessWindow::CmFileNew()
{
if(Animating)					// stop auto-animate
	CmGameAnimate();
Board.Reset();
UpdateAllSquares(TRUE);
}								//CmFileNew
//----------------------------------------------------------------------------
// read the log file (.CHS) of a saved game, for stepping through.
void SChessWindow::CmFileOpen()
{
if(Animating)					// stop auto-animate
	CmGameAnimate();
static TOpenSaveDialog::TData filedata(OFN_FILEMUSTEXIST,		// flags ok
	"Game Move Listings|*.CHS|Board Positions|*.CHB|",0,0,"CHS");
if((TFileOpenDialog(this,filedata)).Execute() != IDOK)
	return;

string s(filedata.FileName);
s.to_upper();
//--------------------------
// .CHB FORMAT: not yet supported
if(s.contains(".CHB"))
{
	return;
}
//--------------------------
CmFileNew();
ifstream infile(s.c_str());
if(!(infile >> Board)) 			// Resets Board and loads Game[] into it.
{
	CmFileNew();
	MessageBox("Invalid move in file, or read failure.","Specified Game Not Loaded",MB_OK);
	return;
}
}                       		//CmFileOpen
//----------------------------------------------------------------------------
void SChessWindow::CmGetUserMove()
{
Square::Deselect();
if(Square::Pending)				// only 1 move at a time
{
	MessageBeep(MB_ICONEXCLAMATION);
	MessageBox("There's a move being processed.","Can't Move",MB_OK | MB_ICONEXCLAMATION);
	return;
}
if(Animating)					// refuse to take a move while auto-animating
	return;
char buf[20] = {0};
// ostrstream(buf,sizeof(buf)) << zpower << ends;
if(TInputDialog(this,"Enter Move","Use A1-H8 format.\nExample: E2-E4",
	buf,sizeof(buf),0,new TPXPictureValidator("&#-&#")).Execute() != IDOK)
		return;
// this validator won't catch everything, but it does set the length and format right
// #error it won't let O-O or O-O-O get through!
// try some variation of this as the filter: "[&#-&#][O-O][O-O-O]"

string s(buf), c("ABCDEFGH"), d("12345678");
if(!c.contains(s[0]) || !c.contains(s[3]) || !d.contains(s[1]) || !d.contains(s[4]))
{
	MessageBeep(MB_ICONEXCLAMATION);
	MessageBox("Invalid move format or nonexistent square.","Can't Move",
				MB_OK | MB_ICONEXCLAMATION);
	return;
}
TPoint from(s[0] - 'A', s[1] - '1');
TPoint   to(s[3] - 'A', s[4] - '1');
Piece* occ = Board.Occupant(from);
if(!occ)
{
		MessageBeep(MB_ICONEXCLAMATION);
		MessageBox("There's no piece on the source square.","Can't Move",
											MB_OK | MB_ICONEXCLAMATION);
}
// create Pending Move, for IdleAction to detect and process
Square::Pending = new Move(occ, from, to, Board.Occupant(to));
}                       	//CmGetUserMove
//----------------------------------------------------------------------------
void SChessWindow::CmUndoMove()
{
Square::Deselect();
if(Square::Pending)				// only 1 move at a time (unsure if necessary)
{
	MessageBeep(MB_ICONEXCLAMATION);
	MessageBox("There's a move being processed.","Can't Undo",MB_OK | MB_ICONEXCLAMATION);
	return;
}
if(Animating)					// refuse to do anything while auto-animating
	return;
if(!Board.UndoMove())			// Board handles everything
{
// the msgbox is a nuissance, but keep for troubleshooting
// 	MessageBox("The game should now be at its starting state.","Can't Undo",MB_OK);
	return;
}
UpdateAllSquares(TRUE);			// put any moved pieces on their proper Squares
}                       		//CmUndoMove
//----------------------------------------------------------------------------
void SChessWindow::CmRedoMove()
{
Square::Deselect();
if(Square::Pending)				// only 1 move at a time (unsure if necessary)
{
	MessageBeep(MB_ICONEXCLAMATION);
	MessageBox("There's a move being processed.","Can't Redo",MB_OK | MB_ICONEXCLAMATION);
	return;
}
if(!Board.RedoMove())			// Board handles everything
{
// the msgbox is a nuissance, but keep for troubleshooting
// 	MessageBox("There are no undone moves to restore.","Can't Redo Move",MB_OK);
	return;
}
UpdateAllSquares(TRUE);			// put any moved pieces on their proper Squares
}                       		//CmRedoMove
//----------------------------------------------------------------------------
// toggle auto-animation on/off.
// Uses a Windows timer instead of IdleAction, for automatically uniform time intervals.
// (and also because IdleAction is used for something else)
// #error program seems remarkably stable, but "Animation" is its own separate mode:
// review carefully for what is actually necessary to disable other functions
// while it's running.
void SChessWindow::CmGameAnimate()
{
Square::Deselect();
if(Square::Pending)				// only 1 move at a time (unsure if necessary)
{
	MessageBeep(MB_ICONEXCLAMATION);
	MessageBox("There's a move being processed.","Can't Start Animating",
				MB_OK | MB_ICONEXCLAMATION);
	return;
}
if(Animating)
{
	if(!KillTimer(1))
		MessageBox("Windows timer not found.",GetApplication()->GetName(),MB_OK);
	Animating = FALSE;
	return;
}
static uint delay = 1000;	// static so previous value is retained, 1 sec is followable
char buf[20];
ostrstream(buf,sizeof(buf)) << delay << ends;
if(TInputDialog(this,"Time Delay Between Moves","In milliseconds (1 to 65535): ",
					 buf,sizeof(buf)).Execute() != IDOK)
	return;
uint i = (uint)atol(buf);
if(i > 0)
	delay = i;
Animating = SetTimer(1, delay, 0);
if(!Animating)
	MessageBox("No timers available.","Can't Animate.",MB_OK | MB_ICONEXCLAMATION);
}                         	//CmGameAnimate
//----------------------------------------------------------------------------
// reset current game back to its beginning
void SChessWindow::CmGameReset()
{
// #error there's a lot of duplicated and nearly-duplicated code like this throughout;
// can one fn do it all? i.e. is it always safe to just discard whatever might be pending?
Square::Deselect();
if(Square::Pending)		// can you just delete and zero it?
{
	MessageBox("There's a move being processed.","Can't Reset Yet",MB_OK);
	return;
}
if(Animating)				   	// turn off animation
	CmGameAnimate();
Board.ResetPieces();
UpdateAllSquares(TRUE);			// put any moved pieces on their proper Squares
}                 				//CmGameReset
//----------------------------------------------------------------------------
// #error these may be unnecessary, duplicating Undo and Redo
// OR these, being more general, could replace the other two.
void SChessWindow::CmGameStepBack()
{
}                          		//CmGameStepBack
//----------------------------------------------------------------------------
void SChessWindow::CmGameStepForward()
{
}                      			//CmGameStepForward
//----------------------------------------------------------------------------
void SChessWindow::CmHelpIndex() { WinHelp("chess.hlp",HELP_INDEX,0); }
//----------------------------------------------------------------------------
void SChessWindow::CmHelpAbout()
{
MessageBox("Copyright 1997-2000 Steven Whitney.",
	GetApplication()->GetName(),MB_OK);
}
//----------------------------------------------------------------------------
void SChessWindow::CmTimerSet()
{
static double interval = 5;    			// default 5 minutes
char buf[20];
ostrstream(buf,sizeof(buf)) << interval << ends;
if(TInputDialog(this,"Timing Interval","Enter minutes (fractions ok) or 0 to turn timer off",
	buf,sizeof(buf)).Execute() != IDOK)
		return;
istrstream(buf) >> interval;
if(interval <= 0)
	sw.AlarmOff();
else
	sw.SetAlarm(interval);
}                             	//CmTimerSet
//----------------------------------------------------------------------------
void SChessWindow::CmTimerReset() { sw.AlarmRestart(); }
								//CmTimerReset
//----------------------------------------------------------------------------
BOOL SChessWindow::CanClose()
{
BOOL status = TLayoutWindow::CanClose();
return(status);
}                     			//CanClose
//----------------------------------------------------------------------------
void SChessWindow::Paint(TDC& pdc, BOOL erase, TRect& invalidarea) // OWLPG:37
{
TLayoutWindow::Paint(pdc,erase,invalidarea);
if(IsIconic())					// minimized, no point doing anything
	return;
}                  				//Paint
//----------------------------------------------------------------------------
BOOL SChessWindow::IdleAction(long idlecount)
{
TLayoutWindow::IdleAction(idlecount);

//----------
// alarm sounds continuously for use as cooking or general timer, so you can't
// accidentally miss it if not there when it starts sounding.
sw.CheckAlarm(TRUE);
//----------

if(Animating)						// refuse to do anything while auto-animating
	return(TRUE);
if(Square::Pending)
{
	Move m(*Square::Pending);		// local copy
	delete Square::Pending;			// delete and zero it immediately
	Square::Pending = 0;
	int result = Board.IsLegal(m);	// IsLegal sets m.CastleType as needed
	if(result < 0)
	{
		string msg;
		switch(result)
		{
			case -1: msg = "Can't castle: King has moved."; break;
			case -2: msg = "Can't castle: Rook has moved."; break;
			case -3: msg = "Can't castle: King is in check."; break;
			case -4: msg = "Can't castle: Intervening square is occupied."; break;
			case -5: msg = "Can't castle: King can't cross threatened square."; break;
			case -6: msg = "It's the other side's turn."; break;
			case -7: msg = "You can't capture your own piece."; break;
			case -8: msg = "You can't capture a King."; break;
			default: msg = "Unknown: Program Error"; break;
		}
		MessageBeep(MB_ICONEXCLAMATION);
		MessageBox(msg.c_str(),"Can't Move",MB_OK | MB_ICONEXCLAMATION);
		return(TRUE);
	}
	//---------------------------------
	if(Board.Game.GetItemsInContainer() > Board.MoveIndex)
	{
		if(MessageBox("There are moves in the existing game beyond this point.\n"
				   "This move will delete all those moves and begin a new\n"
				   "continuation (game) from this point.  Is that what you want?",
				   "Start New Continuation Game?", MB_YESNO | MB_ICONQUESTION) != IDYES)
	   {
			MessageBox("No change will be made.","Move Ignored",MB_OK);
			return(TRUE);
	   }
	}
	// always TRUE, so currently a move sent to IdleAction must always be new
	Board.PostMove(m, TRUE);		// officially post move to the game
	UpdateAllSquares(TRUE); 		// display new stats, and Invalidates

	CmTimerReset();					// restart timer for next move
}							// end if(Square::Pending)
return(TRUE);
}              				//IdleAction
//----------------------------------------------------------------------------
// 						end class SChessWindow
//////////////////////////////////////////////////////////////////////////////
// The application object
class TMyApp : public TApplication
{
public:
	TMyApp(const char far *title) : TApplication(title) {}	// title used in case of error

protected:
	virtual BOOL IdleAction(long idlecount);
	virtual void InitMainWindow()
	{
		TFrameWindow* frame = new TFrameWindow(0,GetName(),new SChessWindow,TRUE);
		frame->Attr.X = 50;
		frame->Attr.Y = 50;
		frame->AssignMenu(TResId(MENU_1));
		frame->Attr.AccelTable = TResId(MENU_1);
		nCmdShow = SW_SHOW;
		SetMainWindow(frame);
		EnableCtl3d(TRUE);
	}
};
//----------------------------------------------------------------------------
// this is the simplest possible fix for the IdleAction bug, for use when time isn't critical
BOOL TMyApp::IdleAction(long idlecount)
{
// passing 0 with every call tricks TFrame into doing what it's supposed to.
TApplication::IdleAction(0);
return TRUE;					// return TRUE to get called back unconditionally
}
//----------------------------------------------------------------------------
// 						end class TMyApp
//////////////////////////////////////////////////////////////////////////////
// OwlMain
int OwlMain(int /* argc */, char** /* argv */)
{
randomize();
string::set_case_sensitive(1);	// for .contains() searches

// Run() calls: InitApplication(), InitInstance() { (which calls: InitMainWindow() },
// then displays main window.
TMyApp* myapp = new TMyApp(AppName);
int retval = myapp->Run();
delete myapp;

return(retval);
}              				//OwlMain
//////////////////////////////////////////////////////////////////////////////

CHESS.DEF

EXETYPE WINDOWS
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 12000
STACKSIZE 12000

CHESS.HPJ

[OPTIONS]
CONTENTS=HID_CONTENTS
TITLE=Chess Help

[CONFIG]
BrowseButtons()

[FILES]
chess.rtf

CHESS.RH

/****************************************************************************

chess.rh

produced by Borland Resource Workshop

*****************************************************************************/

//----------------------------------------------------------------------------
// these are definitions for the starting resources for your new project
//----------------------------------------------------------------------------
#define ACCELERATORS_1	1
#define CM_KEY101	101
#define MAINMENU	1
#define CM_POPUPITEM	101

#define MENU_1	1
#define CM_TIMEROFF	108
#define CM_TIMERRESET	107
#define CM_TIMERSET	106
#define CM_GAMERESET	3
#define CM_GAMEREDOMOVE	105
#define CM_GAMEUNDOMOVE	104
#define CM_GAMESTEPBACK	2
#define CM_GAMESTEPFORWARD	1
#define CM_GETUSERMOVE	103
#define CM_GAMEANIMATE	102
#define CM_OPTIONSREVERSEBOARD	101
#define CM_HELPABOUT	24346
#define CM_HELPINDEX	24341
#define CM_FILEEXIT	24338

//----------------------------------------------------------------------------
// TRANSFILEEDITOR	defines
//----------------------------------------------------------------------------
#define TRANSFILEEDITOR	2000
#define IDC_TRANSEDIT	2001

#define CM_EXIT             24310
#define IDI_OWLAPP          32000

#define CM_CASCADECHILDREN   24361
#define CM_TILECHILDREN      24362
#define CM_TILECHILDRENHORIZ 24363
#define CM_ARRANGEICONS      24364
#define CM_CLOSECHILDREN     24365
#define CM_CREATECHILD       24366

#define IDD_INPUTDIALOG     32514
#define ID_PROMPT           4091
#define ID_INPUT            4090

#define IDS_UNKNOWNEXCEPTION    32767
#define IDS_OWLEXCEPTION        32766
#define IDS_OKTORESUME          32765
#define IDS_UNHANDLEDXMSG       32764
#define IDS_UNKNOWNERROR        32763
#define IDS_NOAPP               32762
#define IDS_OUTOFMEMORY         32761
#define IDS_INVALIDMODULE       32760
#define IDS_INVALIDMAINWINDOW   32759

#define IDS_INVALIDWINDOW       32756
#define IDS_INVALIDCHILDWINDOW  32755
#define IDS_INVALIDCLIENTWINDOW 32754

#define IDS_CLASSREGISTERFAIL   32749
#define IDS_CHILDREGISTERFAIL   32748
#define IDS_WINDOWCREATEFAIL    32747
#define IDS_WINDOWEXECUTEFAIL   32746
#define IDS_CHILDCREATEFAIL     32745

#define IDS_MENUFAILURE         32744
#define IDS_VALIDATORSYNTAX     32743
#define IDS_PRINTERERROR        32742

#define IDS_LAYOUTINCOMPLETE    32741
#define IDS_LAYOUTBADRELWIN     32740

#define IDS_GDIFAILURE          32739
#define IDS_GDIALLOCFAIL        32738
#define IDS_GDICREATEFAIL       32737
#define IDS_GDIRESLOADFAIL      32736
#define IDS_GDIFILEREADFAIL     32735
#define IDS_GDIDELETEFAIL       32734
#define IDS_GDIDESTROYFAIL      32733
#define IDS_INVALIDDIBHANDLE    32732

#define CM_EDITUNDO         24321
#define CM_EDITCUT          24322
#define CM_EDITCOPY         24323
#define CM_EDITPASTE        24324
#define CM_EDITDELETE       24325
#define CM_EDITCLEAR        24326

#define CM_EDITFIND         24351
#define CM_EDITREPLACE      24352
#define CM_EDITFINDNEXT     24353

#define IDS_CANNOTFIND      32540

#define IDM_EDITSEARCH      32540

#define IDA_EDITSEARCH      32540

#define CM_FILENEW          24331
#define CM_FILEOPEN         24332
#define CM_FILESAVE         24333
#define CM_FILESAVEAS       24334

#define CM_FILEPRINT        24337
#define CM_FILEPRINTERSETUP 24338

#define CM_FILECLOSE        24339

#define IDS_UNTITLEDFILE    32550
#define IDS_UNABLEREAD      32551
#define IDS_UNABLEWRITE     32552
#define IDS_FILECHANGED     32553
#define IDS_FILEFILTER      32554

#define IDM_EDITFILE        32550

#define IDA_EDITFILE        32550

#define IDS_VALPXPCONFORM       32520
#define IDS_VALINVALIDCHAR      32521
#define IDS_VALNOTINRANGE       32522
#define IDS_VALNOTINLIST        32523

#define IDB_HSLIDERTHUMB 32000
#define IDB_VSLIDERTHUMB 32001

#define IDS_PRNON            32590
#define IDS_PRNERRORTEMPLATE 32591
#define IDS_PRNOUTOFMEMORY   32592
#define IDS_PRNOUTOFDISK     32593
#define IDS_PRNCANCEL        32594
#define IDS_PRNMGRABORT      32595
#define IDS_PRNGENERROR      32596
#define IDS_PRNERRORCAPTION  32597

#define ID_TITLE  101
#define ID_DEVICE 102
#define ID_PORT   103

#define IDS_MODES  32530

#define IDM_EDITVIEW   32581
#define IDA_EDITVIEW   32581

#define CM_LISTUNDO         24381
#define CM_LISTCUT          24382
#define CM_LISTCOPY         24383
#define CM_LISTPASTE        24384
#define CM_LISTDELETE       24385
#define CM_LISTCLEAR        24386
#define CM_LISTADD          24387
#define CM_LISTEDIT         24388
#define IDM_LISTVIEW        32582
#define IDA_LISTVIEW        32582
#define IDS_LISTNUM         32582

#define CM_FILEREVERT       24335
#define CM_VIEWCREATE       24341

#define IDS_DOCMANAGERFILE  32500
#define IDS_DOCLIST         32501
#define IDS_VIEWLIST        32502
#define IDS_UNTITLED        32503
#define IDS_UNABLEOPEN      32504
#define IDS_UNABLECLOSE     32505
#define IDS_READERROR       32506
#define IDS_WRITEERROR      32507
#define IDS_DOCCHANGED      32508
#define IDS_NOTCHANGED      32509
#define IDS_NODOCMANAGER    32510
#define IDS_NOMEMORYFORVIEW 32511
#define IDS_DUPLICATEDOC    32512

#define IDM_DOCMANAGERFILE   4401

CHESS.RC

/*

CHESS.RC is part of the Chess.cpp project.
Copyright (C)1997-2001 Steven Whitney.
Published under GNU GPL (General Public License) Version 3, with ABSOLUTELY NO WARRANTY.
Initially published by http://25yearsofprogramming.com.

This RC file was probably built upon my OWLRES.RC file. It may contain
resources (or references to resources) that this project doesn't really need.

My original Chess.RC contains some code that is copyrighted by Borland, which I've
removed here. If you get any errors that seem to indicate missing code,
follow the instructions below for what needs to be included from your
own copy of the OWL header and/or RC files.

If you are going to expand the program, it is a good idea to insert all
the code from the indicated files. See the description of the OWLRES.RC
project for the reasons why.

*/

#include "chess.rh"

MENU_1 MENU 
{
 POPUP "&File"
 {
  MENUITEM "&New\tN", CM_FILENEW
  MENUITEM "&Open...\tO", CM_FILEOPEN
  MENUITEM "Save &As...\tS", CM_FILESAVEAS
  MENUITEM SEPARATOR
  MENUITEM "E&xit", CM_FILEEXIT
 }

POPUP "&Game"
 {
  MENUITEM "&Enter Move...\tE", CM_GETUSERMOVE
  MENUITEM "&Undo Move\tU", CM_GAMEUNDOMOVE
  MENUITEM "&Redo Move\tR", CM_GAMEREDOMOVE
  MENUITEM SEPARATOR
  MENUITEM "Reset to &Starting Position", CM_GAMERESET
  MENUITEM "&Animate...\tA", CM_GAMEANIMATE
  MENUITEM "Step &Backward\t<", CM_GAMESTEPBACK
  MENUITEM "Step &Forward\t>", CM_GAMESTEPFORWARD
 }

POPUP "&Options"
 {
  MENUITEM "Reverse &View\tV", CM_OPTIONSREVERSEBOARD
 }

POPUP "&Timer"
 {
  MENUITEM "&Set Interval...\tT", CM_TIMERSET
  MENUITEM "&Reset\t<space>", CM_TIMERRESET
 }

POPUP "&Help"
 {
  MENUITEM "&Index\tF1", CM_HELPINDEX
  MENUITEM "&About...", CM_HELPABOUT
 }

}

STRINGTABLE 
{
 CM_OPTIONSREVERSEBOARD, "Reverse direction from which board is viewed"
 CM_GAMEANIMATE, "Start Auto-Playing a game, with settable time delay"
 CM_GETUSERMOVE, "Make a move"
 CM_GAMESTEPFORWARD, "Same as Redo Move"
 CM_GAMESTEPBACK, "Same as Undo Move"
}


MENU_1 ACCELERATORS 
{
 "v", CM_OPTIONSREVERSEBOARD
 "V", CM_OPTIONSREVERSEBOARD
 "e", CM_GETUSERMOVE
 "E", CM_GETUSERMOVE
 "a", CM_GAMEANIMATE
 "A", CM_GAMEANIMATE
 VK_F1, CM_HELPINDEX, VIRTKEY
 "u", CM_GAMEUNDOMOVE
 "U", CM_GAMEUNDOMOVE
 ",", CM_GAMEUNDOMOVE
 "<", CM_GAMEUNDOMOVE
 "r", CM_GAMEREDOMOVE
 "R", CM_GAMEREDOMOVE
 ".", CM_GAMEREDOMOVE
 ">", CM_GAMEREDOMOVE
 "o", CM_FILEOPEN
 "O", CM_FILEOPEN
 "s", CM_FILESAVEAS
 "S", CM_FILESAVEAS
 "n", CM_FILENEW, ASCII
 "N", CM_FILENEW
 "t", CM_TIMERSET
 VK_SPACE, CM_TIMERRESET, VIRTKEY
}

IDD_INPUTDIALOG DIALOG 276, 23, 127, 98
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_SETFONT
FONT 8, "Helv"
{
 LTEXT "", ID_PROMPT, 10, 8, 105, 36
 EDITTEXT ID_INPUT, 10, 53, 105, 12, WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL
 DEFPUSHBUTTON "&OK", IDOK, 21, 74, 40, 14
 PUSHBUTTON "&Cancel", IDCANCEL, 67, 74, 40, 14
}

TRANSFILEEDITOR DIALOG 97, 24, 170, 201
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_THICKFRAME
CAPTION "Utility Editor"
FONT 8, "MS Sans Serif"
{
 EDITTEXT IDC_TRANSEDIT, 12, 6, 147, 165, ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_BORDER | WS_TABSTOP
 DEFPUSHBUTTON "OK", IDOK, 6, 183, 50, 14
 PUSHBUTTON "Cancel", IDCANCEL, 60, 183, 50, 14
 PUSHBUTTON "Help", IDHELP, 114, 183, 50, 14
}

// If necessary, insert here the definition of the
// Default OWL application icon, which is called...
//----------------------------------------------------------------------------

// IDI_OWLAPP ICON

//----------------------------------------------------------------------------
// If necessary, insert here the code from...
//   include\owl\except.rc (Exception string resources)

//----------------------------------------------------------------------------
// If necessary, insert here the code from...
//   include\owl\statusba.rc

//----------------------------------------------------------------------------
// If necessary, insert here the code from...
//   include\owl\validate.rc

//----------------------------------------------------------------------------
// If necessary, insert here the code from...
//   include\owl\editsear.rc

//----------------------------------------------------------------------------
// If necessary, insert here the code from...
//   include\owl\editfile.rc

//----------------------------------------------------------------------------
// If necessary, insert here the code from...
//   include\owl\editview.rc

//----------------------------------------------------------------------------
// If necessary, insert here the code from...
//   include\owl\docview.rc

STRINGTABLE 
{
 CM_FILENEW, "Initialize a new game"
 CM_FILEOPEN, "Load the moves from a game listing"
 CM_FILESAVE, "Saves this document"
 CM_FILESAVEAS, "Save this game's moves or its current board position"
 CM_FILECLOSE, "Closes this document"
 CM_EXIT, "Quits the application, prompts to save documents"
 CM_GAMEUNDOMOVE, "Undo the most recent move"
 CM_GAMEREDOMOVE, "Restore (replay) the latest Undone move."
 CM_FILEEXIT, "Quit this application"
 CM_GAMERESET, "Reset the current game back to its start."
 CM_TIMERSET, "Set timer interval and start timing (or turn timer off)"
 CM_TIMERRESET, "Stop the current timing interval and start a new full-length one"
 CM_TIMEROFF, "Stop the timer accessory"
}

 

Valid HTML 4.01 Transitional Valid CSS
Yahoo! Search
Search the web Search this site
View content labeling at ICRA.
Copyright ©2010 Steven Whitney. Last modified 03/22/2010.