diff --git a/BBSCHESS/BBSCHESS.FRM b/BBSCHESS/BBSCHESS.FRM new file mode 100644 index 0000000..a0f5b5c --- /dev/null +++ b/BBSCHESS/BBSCHESS.FRM @@ -0,0 +1,1082 @@ +VERSION 5.00 +Begin VB.Form frmBbsChess + BorderStyle = 1 'Fixed Single + Caption = "BBS Chess" + ClientHeight = 4425 + ClientLeft = 45 + ClientTop = 330 + ClientWidth = 7050 + BeginProperty Font + Name = "宋体" + Size = 9 + Charset = 134 + Weight = 400 + Underline = 0 'False + Italic = 0 'False + Strikethrough = 0 'False + EndProperty + Icon = "BBSCHESS.frx":0000 + MaxButton = 0 'False + ScaleHeight = 4425 + ScaleWidth = 7050 + StartUpPosition = 3 'Windows Default + Begin VB.CommandButton btnExit + Cancel = -1 'True + Caption = "退出" + Height = 375 + Left = 6000 + TabIndex = 22 + Top = 3600 + Width = 975 + End + Begin VB.CommandButton btnCopy + Caption = "复制棋盘" + Height = 375 + Left = 6000 + TabIndex = 21 + Top = 3120 + Width = 975 + End + Begin VB.CommandButton btnLeftRight + Caption = "左右翻转" + Height = 375 + Left = 6000 + TabIndex = 20 + Top = 2520 + Width = 975 + End + Begin VB.CommandButton btnUpDown + Caption = "上下翻转" + Height = 375 + Left = 6000 + TabIndex = 19 + Top = 2040 + Width = 975 + End + Begin VB.Frame frmPieces + Caption = "放置棋子" + Height = 3735 + Left = 120 + TabIndex = 1 + Top = 240 + Width = 1815 + Begin VB.OptionButton optPieces + Height = 255 + Index = 12 + Left = 960 + TabIndex = 14 + Top = 3240 + Width = 255 + End + Begin VB.OptionButton optPieces + Height = 255 + Index = 11 + Left = 960 + TabIndex = 13 + Top = 2760 + Width = 255 + End + Begin VB.OptionButton optPieces + Height = 255 + Index = 10 + Left = 960 + TabIndex = 12 + Top = 2280 + Width = 255 + End + Begin VB.OptionButton optPieces + Height = 255 + Index = 9 + Left = 960 + TabIndex = 11 + Top = 1800 + Width = 255 + End + Begin VB.OptionButton optPieces + Height = 255 + Index = 8 + Left = 960 + TabIndex = 10 + Top = 1320 + Width = 255 + End + Begin VB.OptionButton optPieces + Height = 255 + Index = 7 + Left = 960 + TabIndex = 9 + Top = 840 + Width = 255 + End + Begin VB.OptionButton optPieces + Height = 255 + Index = 6 + Left = 120 + TabIndex = 8 + Top = 3240 + Width = 255 + End + Begin VB.OptionButton optPieces + Height = 255 + Index = 5 + Left = 120 + TabIndex = 7 + Top = 2760 + Width = 255 + End + Begin VB.OptionButton optPieces + Height = 255 + Index = 4 + Left = 120 + TabIndex = 6 + Top = 2280 + Width = 255 + End + Begin VB.OptionButton optPieces + Height = 255 + Index = 3 + Left = 120 + TabIndex = 5 + Top = 1800 + Width = 255 + End + Begin VB.OptionButton optPieces + Height = 255 + Index = 2 + Left = 120 + TabIndex = 4 + Top = 1320 + Width = 255 + End + Begin VB.OptionButton optPieces + Height = 255 + Index = 1 + Left = 120 + TabIndex = 3 + Top = 840 + Width = 255 + End + Begin VB.OptionButton optPieces + Height = 255 + Index = 0 + Left = 480 + TabIndex = 2 + Top = 360 + Value = -1 'True + Width = 255 + End + Begin VB.Image imgPieces + Appearance = 0 'Flat + BorderStyle = 1 'Fixed Single + Height = 510 + Index = 0 + Left = 840 + Picture = "BBSCHESS.frx":0ABA + Top = 240 + Width = 510 + End + Begin VB.Image imgPieces + Height = 480 + Index = 12 + Left = 1200 + Picture = "BBSCHESS.frx":0DC4 + Top = 3120 + Width = 480 + End + Begin VB.Image imgPieces + Height = 480 + Index = 11 + Left = 1200 + Picture = "BBSCHESS.frx":10CE + Top = 2640 + Width = 480 + End + Begin VB.Image imgPieces + Height = 480 + Index = 10 + Left = 1200 + Picture = "BBSCHESS.frx":13D8 + Top = 2160 + Width = 480 + End + Begin VB.Image imgPieces + Height = 480 + Index = 9 + Left = 1200 + Picture = "BBSCHESS.frx":16E2 + Top = 1680 + Width = 480 + End + Begin VB.Image imgPieces + Height = 480 + Index = 8 + Left = 1200 + Picture = "BBSCHESS.frx":19EC + Top = 1200 + Width = 480 + End + Begin VB.Image imgPieces + Height = 480 + Index = 7 + Left = 1200 + Picture = "BBSCHESS.frx":1CF6 + Top = 720 + Width = 480 + End + Begin VB.Image imgPieces + Height = 480 + Index = 6 + Left = 360 + Picture = "BBSCHESS.frx":2000 + Top = 3120 + Width = 480 + End + Begin VB.Image imgPieces + Height = 480 + Index = 5 + Left = 360 + Picture = "BBSCHESS.frx":230A + Top = 2640 + Width = 480 + End + Begin VB.Image imgPieces + Height = 480 + Index = 4 + Left = 360 + Picture = "BBSCHESS.frx":2614 + Top = 2160 + Width = 480 + End + Begin VB.Image imgPieces + Height = 480 + Index = 3 + Left = 360 + Picture = "BBSCHESS.frx":291E + Top = 1680 + Width = 480 + End + Begin VB.Image imgPieces + Height = 480 + Index = 2 + Left = 360 + Picture = "BBSCHESS.frx":2C28 + Top = 1200 + Width = 480 + End + Begin VB.Image imgPieces + Height = 480 + Index = 1 + Left = 360 + Picture = "BBSCHESS.frx":2F32 + Top = 720 + Width = 480 + End + End + Begin VB.CommandButton btnCounter + Caption = "逆时针转" + Height = 375 + Left = 6000 + TabIndex = 18 + Top = 1560 + Width = 975 + End + Begin VB.CommandButton btnClockwise + Caption = "顺时针转" + Height = 375 + Left = 6000 + TabIndex = 17 + Top = 1080 + Width = 975 + End + Begin VB.CommandButton btnClear + Caption = "清空棋盘" + Height = 375 + Left = 6000 + TabIndex = 16 + Top = 600 + Width = 975 + End + Begin VB.CommandButton btnStart + Caption = "起始局面" + Height = 375 + Left = 6000 + TabIndex = 15 + Top = 120 + Width = 975 + End + Begin VB.TextBox txtFen + Height = 270 + Left = 120 + TabIndex = 0 + Top = 4080 + Width = 6855 + End + Begin VB.Image imgSquares + Height = 495 + Index = 63 + Left = 5400 + Top = 3480 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 62 + Left = 4920 + Top = 3480 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 61 + Left = 4440 + Top = 3480 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 60 + Left = 3960 + Top = 3480 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 59 + Left = 3480 + Top = 3480 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 58 + Left = 3000 + Top = 3480 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 57 + Left = 2520 + Top = 3480 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 56 + Left = 2040 + Top = 3480 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 55 + Left = 5400 + Top = 3000 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 54 + Left = 4920 + Top = 3000 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 53 + Left = 4440 + Top = 3000 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 52 + Left = 3960 + Top = 3000 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 51 + Left = 3480 + Top = 3000 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 50 + Left = 3000 + Top = 3000 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 49 + Left = 2520 + Top = 3000 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 48 + Left = 2040 + Top = 3000 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 47 + Left = 5400 + Top = 2520 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 46 + Left = 4920 + Top = 2520 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 45 + Left = 4440 + Top = 2520 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 44 + Left = 3960 + Top = 2520 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 43 + Left = 3480 + Top = 2520 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 42 + Left = 3000 + Top = 2520 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 41 + Left = 2520 + Top = 2520 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 40 + Left = 2040 + Top = 2520 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 39 + Left = 5400 + Top = 2040 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 38 + Left = 4920 + Top = 2040 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 37 + Left = 4440 + Top = 2040 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 36 + Left = 3960 + Top = 2040 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 35 + Left = 3480 + Top = 2040 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 34 + Left = 3000 + Top = 2040 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 33 + Left = 2520 + Top = 2040 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 32 + Left = 2040 + Top = 2040 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 31 + Left = 5400 + Top = 1560 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 30 + Left = 4920 + Top = 1560 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 29 + Left = 4440 + Top = 1560 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 28 + Left = 3960 + Top = 1560 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 27 + Left = 3480 + Top = 1560 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 26 + Left = 3000 + Top = 1560 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 25 + Left = 2520 + Top = 1560 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 24 + Left = 2040 + Top = 1560 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 23 + Left = 5400 + Top = 1080 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 22 + Left = 4920 + Top = 1080 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 21 + Left = 4440 + Top = 1080 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 20 + Left = 3960 + Top = 1080 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 19 + Left = 3480 + Top = 1080 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 18 + Left = 3000 + Top = 1080 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 17 + Left = 2520 + Top = 1080 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 16 + Left = 2040 + Top = 1080 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 15 + Left = 5400 + Top = 600 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 14 + Left = 4920 + Top = 600 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 13 + Left = 4440 + Top = 600 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 12 + Left = 3960 + Top = 600 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 11 + Left = 3480 + Top = 600 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 10 + Left = 3000 + Top = 600 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 9 + Left = 2520 + Top = 600 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 8 + Left = 2040 + Top = 600 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 7 + Left = 5400 + Top = 120 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 6 + Left = 4920 + Top = 120 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 5 + Left = 4440 + Top = 120 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 4 + Left = 3960 + Top = 120 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 3 + Left = 3480 + Top = 120 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 2 + Left = 3000 + Top = 120 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 1 + Left = 2520 + Top = 120 + Width = 495 + End + Begin VB.Image imgSquares + Height = 495 + Index = 0 + Left = 2040 + Top = 120 + Width = 495 + End + Begin VB.Image imgBoard + Height = 3840 + Left = 2040 + Picture = "BBSCHESS.frx":323C + Top = 120 + Width = 3840 + End +End +Attribute VB_Name = "frmBbsChess" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False +' BBS Chess - A Chess Position Edit Program +' Designed by Morning Yellow, Version: 1.1, Last Modified: May. 2008 +' Copyright (C) 2004-2008 www.elephantbase.net +' +' This library is free software; you can redistribute it and/or +' modify it under the terms of the GNU Lesser General Public +' License as published by the Free Software Foundation; either +' version 2.1 of the License, or (at your option) any later version. +' +' This library 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 +' Lesser General Public License for more details. +' +' You should have received a copy of the GNU Lesser General Public +' License along with this library; if not, write to the Free Software +' Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Option Explicit + +Private szFen As String, szTail As String, nSelected As Integer +Private nBoard(1 To 8, 1 To 8) As Integer + +Private Sub Swap(ByRef dst As Variant, ByRef src As Variant) + +Dim tmp As Variant +tmp = dst +dst = src +src = tmp + +End Sub + +Private Function Piece2Char(ByVal nPiece As Integer) As String + +If nPiece > 6 Then + Piece2Char = LCase(Piece2Char(nPiece - 6)) +Else + Piece2Char = Choose(nPiece, "K", "Q", "B", "N", "R", "P") +End If + +End Function + +Private Sub Board2Fen() + +Dim i As Integer, j As Integer, k As Integer, l As Integer +k = 0 +szFen = "" +For i = 1 To 8 + For j = 1 To 8 + l = nBoard(i, j) + If l = 0 Then + k = k + 1 + Else + If k > 0 Then + szFen = szFen + Chr(k + 48) + k = 0 + End If + szFen = szFen + Piece2Char(l) + End If + Next + If k > 0 Then + szFen = szFen + Chr(k + 48) + k = 0 + End If + If i < 8 Then + szFen = szFen + "/" + End If +Next +szFen = szFen + " " + szTail + +End Sub + +Private Function Char2Piece(ByVal Char As String) As Integer + +Select Case Char +Case "K" + Char2Piece = 1 +Case "Q" + Char2Piece = 2 +Case "B" + Char2Piece = 3 +Case "N" + Char2Piece = 4 +Case "R" + Char2Piece = 5 +Case "P" + Char2Piece = 6 +Case Else + Char2Piece = 0 +End Select + +End Function + +Private Sub Fen2Board() + +Dim i As Integer, j As Integer, k As Integer, l As Integer, szChar As String +i = InStr(szFen, " ") +szTail = Right(szFen, Len(szFen) - i) +j = 1 +k = 1 +For i = 1 To Len(szFen) + szChar = Mid(szFen, i, 1) + Select Case szChar + Case "/" + j = j + 1 + If j > 8 Then + Exit For + End If + k = 1 + Case "1" To "9" + For l = 1 To Asc(szChar) - 48 + nBoard(j, k) = 0 + k = k + 1 + If k > 8 Then + Exit For + End If + Next + Case "A" To "Z" + If k <= 8 Then + nBoard(j, k) = Char2Piece(szChar) + k = k + 1 + End If + Case "a" To "z" + If k <= 8 Then + nBoard(j, k) = 6 + Char2Piece(UCase(szChar)) + k = k + 1 + End If + Case Else + Exit For + End Select +Next + +End Sub + +Private Sub DrawSquare(ByVal x As Integer, ByVal y As Integer) + +imgSquares((x - 1) * 8 + (y - 1)).Picture = imgPieces(nBoard(x, y)).Picture + +End Sub + +Private Sub BoardFlush() + +Dim i As Integer, j As Integer +For i = 1 To 8 + For j = 1 To 8 + DrawSquare i, j + Next +Next + +End Sub + +Private Function Piece2Chin(ByVal nPiece As Integer, ByVal bBlackSq As Boolean) As String + +Piece2Chin = Chr(27) + Chr(27) + IIf(nPiece > 6, "[0;", "[1;") + IIf(bBlackSq, "43;", "46;") + IIf(nPiece > 6, "30m", "37m") + Choose(IIf(nPiece > 6, nPiece - 6, nPiece) + 1, " ", "王", "后", "象", "马", "车", "兵") + +End Function + +Private Sub Form_Load() + +nSelected = 0 +btnStart_Click + +End Sub + +Private Sub btnStart_Click() + +szFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" +Fen2Board +txtFen.Text = szFen +BoardFlush + +End Sub + +Private Sub btnClear_Click() + +szFen = "8/8/8/8/8/8/8/8 w - - 0 1" +Fen2Board +txtFen.Text = szFen +BoardFlush + +End Sub + +Private Sub btnClockwise_Click() + +Dim i As Integer, j As Integer +Dim nTempBoard(1 To 8, 1 To 8) As Integer +For i = 1 To 8 + For j = 1 To 8 + nTempBoard(i, j) = nBoard(9 - j, i) + Next +Next +For i = 1 To 8 + For j = 1 To 8 + nBoard(i, j) = nTempBoard(i, j) + Next +Next +Board2Fen +txtFen.Text = szFen +BoardFlush + +End Sub + +Private Sub btnCounter_Click() + +Dim i As Integer, j As Integer +Dim nTempBoard(1 To 8, 1 To 8) As Integer +For i = 1 To 8 + For j = 1 To 8 + nTempBoard(i, j) = nBoard(j, 9 - i) + Next +Next +For i = 1 To 8 + For j = 1 To 8 + nBoard(i, j) = nTempBoard(i, j) + Next +Next +Board2Fen +txtFen.Text = szFen +BoardFlush + +End Sub + +Private Sub btnUpDown_Click() + +Dim i As Integer, j As Integer +For i = 1 To 4 + For j = 1 To 8 + Swap nBoard(i, j), nBoard(9 - i, j) + Next +Next +Board2Fen +txtFen.Text = szFen +BoardFlush + +End Sub + +Private Sub btnLeftRight_Click() + +Dim i As Integer, j As Integer +For i = 1 To 8 + For j = 1 To 4 + Swap nBoard(i, j), nBoard(i, 9 - j) + Next +Next +Board2Fen +txtFen.Text = szFen +BoardFlush + +End Sub + +Private Sub btnCopy_Click() + +Dim i As Integer, j As Integer, sz As String +sz = "" +For i = 1 To 8 + For j = 1 To 8 + sz = sz + Piece2Chin(nBoard(i, j), (i + j) Mod 2 = 0) + Next + sz = sz + Chr(27) + Chr(27) + "[0m" + vbCrLf +Next +Clipboard.Clear +Clipboard.SetText sz + szFen + vbCrLf + "欢迎访问www.elephantbase.net" + vbCrLf +MsgBox "文本棋盘已复制到剪贴板。", vbInformation + +End Sub + +Private Sub btnExit_Click() + +Unload Me + +End Sub + +Private Sub txtFen_Change() + +If szFen <> txtFen.Text Then + szFen = txtFen.Text + Fen2Board + BoardFlush +End If + +End Sub + +Private Sub optPieces_Click(nIndex As Integer) + +nSelected = nIndex +optPieces(nIndex).Value = True + +End Sub + +Private Sub imgPieces_Click(nIndex As Integer) + +nSelected = nIndex +optPieces(nIndex).Value = True + +End Sub + +Private Sub imgSquares_MouseDown(nIndex As Integer, nButton As Integer, nShift As Integer, x As Single, y As Single) + +If nButton = vbLeftButton Then + nBoard(nIndex \ 8 + 1, nIndex Mod 8 + 1) = nSelected +ElseIf nButton = vbRightButton Then + nBoard(nIndex \ 8 + 1, nIndex Mod 8 + 1) = 0 +End If +DrawSquare nIndex \ 8 + 1, nIndex Mod 8 + 1 +Board2Fen +txtFen.Text = szFen + +End Sub diff --git a/BBSCHESS/BBSCHESS.FRX b/BBSCHESS/BBSCHESS.FRX new file mode 100644 index 0000000..6cf8931 Binary files /dev/null and b/BBSCHESS/BBSCHESS.FRX differ diff --git a/BBSCHESS/BBSCHESS.GIF b/BBSCHESS/BBSCHESS.GIF new file mode 100644 index 0000000..acb1912 Binary files /dev/null and b/BBSCHESS/BBSCHESS.GIF differ diff --git a/BBSCHESS/BBSCHESS.VBP b/BBSCHESS/BBSCHESS.VBP new file mode 100644 index 0000000..d7082ae --- /dev/null +++ b/BBSCHESS/BBSCHESS.VBP @@ -0,0 +1,42 @@ +Type=Exe +Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\WINDOWS\system32\stdole2.tlb#OLE Automation +Form=BBSCHESS.FRM +IconForm="frmBbsChess" +Startup="frmBbsChess" +HelpFile="" +Title="BBS Chess" +ExeName32="BBSCHESS.EXE" +Command32="" +Name="prjBbsChess" +HelpContextID="0" +CompatibleMode="0" +MajorVer=1 +MinorVer=10 +RevisionVer=0 +AutoIncrementVer=0 +ServerSupportFiles=0 +VersionComments="BBS Chess, (C) 2004-2008 www.elephantbase.net" +VersionCompanyName="Author: Morning Yellow (Address: Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)" +VersionFileDescription="BBS Chess" +VersionLegalCopyright="BBS Chess, (C) 2004-2008 www.elephantbase.net" +VersionLegalTrademarks="www.elephantbase.net" +VersionProductName="BBS Chess" +CompilationType=0 +OptimizationType=0 +FavorPentiumPro(tm)=0 +CodeViewDebugInfo=0 +NoAliasing=0 +BoundsCheck=0 +OverflowCheck=0 +FlPointCheck=0 +FDIVCheck=0 +UnroundedFP=0 +StartMode=0 +Unattended=0 +Retained=0 +ThreadPerObject=0 +MaxNumberOfThreads=1 +DebugStartupOption=0 + +[MS Transaction Server] +AutoRefresh=1 diff --git a/BBSCHESS/README.TXT b/BBSCHESS/README.TXT new file mode 100644 index 0000000..930adf0 --- /dev/null +++ b/BBSCHESS/README.TXT @@ -0,0 +1,15 @@ +BBS Chess简介 + +  BBS Chess 是国际象棋局面设置工具,它具有以下特点: +  1. 具有摆放棋子的功能(附带设置起始局面、清空棋盘以及各种翻转功能); +  2. 支持FEN格式,可作为WinBoard等通用软件的辅助工具; +  3. 能产生BBS的ANSI代码,为棋盘发布在BBS上提供方便; +  4. 其 Visual Basic 的源代码是公开的,方便广大象棋爱好者根据自己需要重新设计。 + +  注:源程序被 Visual Basic 6 编译过,运行时如果出现找不到“MSVBVM60.DLL”的错误,就表明系统没有安装VB6运行包,安装后即可正常运行。 +  BBS Chess 的下载地址:http://www.elephantbase.net/download/bbschess.rar +  VB6 运行包的下载地址:http://www.elephantbase.net/download/vbrun60.exe + +  有关FEN格式的规范,可参阅:http://www.elephantbase.net/protocol/pgnfen2.htm +  如有其他疑问,可发eMail询问:webmaster@elephantbase.net +  欢迎广大网友对 BBS Chess 提出意见和建议。 \ No newline at end of file diff --git a/BBSCHESS/RES/APP.ICO b/BBSCHESS/RES/APP.ICO new file mode 100644 index 0000000..4eb0792 Binary files /dev/null and b/BBSCHESS/RES/APP.ICO differ diff --git a/BBSCHESS/RES/BB.ICO b/BBSCHESS/RES/BB.ICO new file mode 100644 index 0000000..7bd58b7 Binary files /dev/null and b/BBSCHESS/RES/BB.ICO differ diff --git a/BBSCHESS/RES/BK.ICO b/BBSCHESS/RES/BK.ICO new file mode 100644 index 0000000..be1e007 Binary files /dev/null and b/BBSCHESS/RES/BK.ICO differ diff --git a/BBSCHESS/RES/BN.ICO b/BBSCHESS/RES/BN.ICO new file mode 100644 index 0000000..9ed2a4f Binary files /dev/null and b/BBSCHESS/RES/BN.ICO differ diff --git a/BBSCHESS/RES/BP.ICO b/BBSCHESS/RES/BP.ICO new file mode 100644 index 0000000..92fb13d Binary files /dev/null and b/BBSCHESS/RES/BP.ICO differ diff --git a/BBSCHESS/RES/BQ.ICO b/BBSCHESS/RES/BQ.ICO new file mode 100644 index 0000000..e80136d Binary files /dev/null and b/BBSCHESS/RES/BQ.ICO differ diff --git a/BBSCHESS/RES/BR.ICO b/BBSCHESS/RES/BR.ICO new file mode 100644 index 0000000..040d468 Binary files /dev/null and b/BBSCHESS/RES/BR.ICO differ diff --git a/BBSCHESS/RES/OO.ICO b/BBSCHESS/RES/OO.ICO new file mode 100644 index 0000000..d3f65ab Binary files /dev/null and b/BBSCHESS/RES/OO.ICO differ diff --git a/BBSCHESS/RES/WB.ICO b/BBSCHESS/RES/WB.ICO new file mode 100644 index 0000000..d062b8c Binary files /dev/null and b/BBSCHESS/RES/WB.ICO differ diff --git a/BBSCHESS/RES/WK.ICO b/BBSCHESS/RES/WK.ICO new file mode 100644 index 0000000..b419e00 Binary files /dev/null and b/BBSCHESS/RES/WK.ICO differ diff --git a/BBSCHESS/RES/WN.ICO b/BBSCHESS/RES/WN.ICO new file mode 100644 index 0000000..a89c841 Binary files /dev/null and b/BBSCHESS/RES/WN.ICO differ diff --git a/BBSCHESS/RES/WP.ICO b/BBSCHESS/RES/WP.ICO new file mode 100644 index 0000000..d3d93cb Binary files /dev/null and b/BBSCHESS/RES/WP.ICO differ diff --git a/BBSCHESS/RES/WQ.ICO b/BBSCHESS/RES/WQ.ICO new file mode 100644 index 0000000..db3b0cf Binary files /dev/null and b/BBSCHESS/RES/WQ.ICO differ diff --git a/BBSCHESS/RES/WR.ICO b/BBSCHESS/RES/WR.ICO new file mode 100644 index 0000000..a6aeb95 Binary files /dev/null and b/BBSCHESS/RES/WR.ICO differ diff --git a/BOOK/BOOK.DAT b/BOOK/BOOK.DAT new file mode 100644 index 0000000..8f5eeae Binary files /dev/null and b/BOOK/BOOK.DAT differ diff --git a/BOOK/MAKEBOOK.CPP b/BOOK/MAKEBOOK.CPP new file mode 100644 index 0000000..e787704 --- /dev/null +++ b/BOOK/MAKEBOOK.CPP @@ -0,0 +1,343 @@ +/* +ElephantEye Book Maker - a Chinese Chess Book Maker Program for ElephantEye +Designed by Morning Yellow, Version: 3.1, Last Modified: Nov. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include "../base/base.h" +#include "../eleeye/position.h" +#include "../eleeye/book.h" +#include "../cchess/cchess.h" +#include "../cchess/pgnfile.h" + +extern "C" __declspec(dllexport) BOOL WINAPI MakeBook(LPCSTR szPath, LPCSTR szFile, + LONG nWin, LONG nDraw, LONG nLoss, LONG nRatio); +extern "C" __declspec(dllexport) LONG WINAPI GetBookMoves(const PositionStruct *lppos, + LPCSTR szFile, LPLONG lpMoves, LPLONG lpValues); + +struct TempStruct { + uint32_t dwZobristLock0, dwZobristLock1; + int mv, vl; + TempStruct() { + } + TempStruct(const TempStruct &tmp) { + dwZobristLock0 = tmp.dwZobristLock0; + dwZobristLock1 = tmp.dwZobristLock1; + mv = tmp.mv; + vl = tmp.vl; + } + TempStruct(const PositionStruct &pos, int mvArg, int vlArg) { + dwZobristLock0 = pos.zobr.dwLock0; + dwZobristLock1 = pos.zobr.dwLock1; + mv = mvArg; + vl = vlArg; + } + bool operator <(TempStruct tmp) const { + return dwZobristLock1 < tmp.dwZobristLock1 ? true : + dwZobristLock1 > tmp.dwZobristLock1 ? false : + dwZobristLock0 < tmp.dwZobristLock0 ? true : + dwZobristLock0 > tmp.dwZobristLock0 ? false : mv < tmp.mv; + } +}; + +static struct { + FILE *fpBookFile, *fpTempFile; + int nTempLen; + int nWin, nDraw, nLoss, nRatio; + TempStruct *TempBuffer; +} MakeBook2; + +inline void ReadTemp(int nPtr, TempStruct *lptmp, int nLen) { + fseek(MakeBook2.fpTempFile, nPtr * sizeof(TempStruct), SEEK_SET); + fread(lptmp, sizeof(TempStruct), nLen, MakeBook2.fpTempFile); +} + +inline void WriteTemp(int nPtr, const TempStruct *lptmp, int nLen) { + fseek(MakeBook2.fpTempFile, nPtr * sizeof(TempStruct), SEEK_SET); + fwrite(lptmp, sizeof(TempStruct), nLen, MakeBook2.fpTempFile); +} + +inline TempStruct GetTemp(int nPtr) { + TempStruct tmp; + ReadTemp(nPtr, &tmp, 1); + return tmp; +} + +inline void SetTemp(int nPtr, const TempStruct &tmp) { + WriteTemp(nPtr, &tmp, 1); +} + +inline void AddTemp(const TempStruct &tmp) { + SetTemp(MakeBook2.nTempLen, tmp); + MakeBook2.nTempLen ++; +} + +inline int MoveValue(int sd, int nResult) { + switch (nResult) { + case 1: + return sd == 0 ? MakeBook2.nWin : MakeBook2.nLoss; + case 2: + return MakeBook2.nDraw; + case 3: + return sd == 0 ? MakeBook2.nLoss : MakeBook2.nWin; + default: + return 0; + } +} + +static void ParseFile(const char *szFilePath) { + int i, mv, mvMirror, nComp; + PositionStruct pos, posMirror; + PgnFileStruct pgn; + + if (pgn.Read(szFilePath)) { + pos = posMirror = pgn.posStart; + posMirror.Mirror(); + for (i = 0; i < pgn.nMaxMove; i ++) { + mv = pgn.wmvMoveTable[i + 1]; + mvMirror = MOVE_MIRROR(mv); + if (pos.zobr.dwLock1 < posMirror.zobr.dwLock1) { + nComp = -1; + } else if (pos.zobr.dwLock1 > posMirror.zobr.dwLock1) { + nComp = 1; + } else { + if (pos.zobr.dwLock0 < posMirror.zobr.dwLock0) { + nComp = -1; + } else if (pos.zobr.dwLock0 > posMirror.zobr.dwLock0) { + nComp = 1; + } else { + nComp = 0; + } + } + if (nComp <= 0) { + AddTemp(TempStruct(pos, mv, MoveValue(pos.sdPlayer, pgn.nResult))); + } + if (nComp >= 0) { + AddTemp(TempStruct(posMirror, mvMirror, MoveValue(pos.sdPlayer, pgn.nResult))); + } + if (pos.ucpcSquares[DST(mv)] == 0) { + pos.MakeMove(mv); + } else { + pos.MakeMove(mv); + pos.SetIrrev(); + } + if (posMirror.ucpcSquares[DST(mvMirror)] == 0) { + posMirror.MakeMove(mvMirror); + } else { + posMirror.MakeMove(mvMirror); + posMirror.SetIrrev(); + } + } + } +} + +static void SearchFolder(const char *szFolderPath); + +static void SearchFile(const char *szFilePath, const WIN32_FIND_DATA &wfd) { + if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + if (strlen(szFilePath) > 4) { + if (strnicmp(szFilePath + strlen(szFilePath) - 4, ".PGN", 4) == 0) { + ParseFile(szFilePath); + } + } + } else { + if (strcmp(wfd.cFileName, ".") != 0 && strcmp(wfd.cFileName, "..") != 0) { + SearchFolder(szFilePath); + } + } +} + +static void SearchFolder(const char *szFolderPath) { + char szFilePath[1024]; + WIN32_FIND_DATA wfd; + HANDLE hFind; + char *lpFilePath; + + strcpy(szFilePath, szFolderPath); + lpFilePath = szFilePath + strlen(szFolderPath); + if (*(lpFilePath - 1) == '\\') { + lpFilePath --; + } + strcpy(lpFilePath, "\\*"); + lpFilePath ++; + hFind = FindFirstFile(szFilePath, &wfd); + if (hFind != INVALID_HANDLE_VALUE) { + strcpy(lpFilePath, wfd.cFileName); + SearchFile(szFilePath, wfd); + while (FindNextFile(hFind, &wfd)) { + strcpy(lpFilePath, wfd.cFileName); + SearchFile(szFilePath, wfd); + } + } + FindClose(hFind); +} + +static const int TEMP_BUFFER_SIZE = 1048576; + +static void SortMem(int low, int high) { + TempStruct tmpMid, tmpSwap; + int lowMid, highMid; + + lowMid = low; + highMid = high; + tmpMid = MakeBook2.TempBuffer[(low + high) / 2]; + while (lowMid <= highMid) { + while (lowMid < high && MakeBook2.TempBuffer[lowMid] < tmpMid) { + lowMid ++; + } + while (low < highMid && tmpMid < MakeBook2.TempBuffer[highMid]) { + highMid --; + } + if (lowMid <= highMid) { + SWAP(MakeBook2.TempBuffer[lowMid], MakeBook2.TempBuffer[highMid]); + lowMid ++; + highMid --; + } + } + + if (low < highMid) { + SortMem(low, highMid); + } + if (lowMid < high) { + SortMem(lowMid, high); + } +} + +static void SortTemp(int low, int high) { + TempStruct tmpMid, tmpSwap; + int lowMid, highMid, nLen; + + nLen = high - low + 1; + if (nLen > TEMP_BUFFER_SIZE) { + lowMid = low; + highMid = high; + tmpMid = GetTemp((low + high) / 2); + while (lowMid <= highMid) { + while (lowMid < high && GetTemp(lowMid) < tmpMid) { + lowMid ++; + } + while (low < highMid && tmpMid < GetTemp(highMid)) { + highMid --; + } + if (lowMid <= highMid) { + tmpSwap = GetTemp(lowMid); + SetTemp(lowMid, GetTemp(highMid)); + SetTemp(highMid, tmpSwap); + lowMid ++; + highMid --; + } + } + + if (low < highMid) { + SortTemp(low, highMid); + } + if (lowMid < high) { + SortTemp(lowMid, high); + } + + } else { + ReadTemp(low, MakeBook2.TempBuffer, nLen); + if (nLen > 1) { + SortMem(0, nLen - 1); + } + WriteTemp(low, MakeBook2.TempBuffer, nLen); + } +} + +static void MakeBook() { + int i; + TempStruct tmpLast, tmp; + BookStruct bk; + + tmpLast.dwZobristLock0 = tmpLast.dwZobristLock1 = 0; + tmpLast.mv = tmpLast.vl = 0; + for (i = 0; i < MakeBook2.nTempLen; i ++) { + tmp = GetTemp(i); + if (tmpLast < tmp) { + if (tmpLast.vl >= MakeBook2.nRatio) { + bk.dwZobristLock = tmpLast.dwZobristLock1; + bk.wmv = tmpLast.mv; + bk.wvl = tmpLast.vl / MakeBook2.nRatio; + fwrite(&bk, sizeof(BookStruct), 1, MakeBook2.fpBookFile); + } + tmpLast = tmp; + } else { + tmpLast.vl += tmp.vl; + } + } + if (tmpLast.vl >= MakeBook2.nRatio) { + bk.dwZobristLock = tmpLast.dwZobristLock1; + bk.wmv = tmpLast.mv; + bk.wvl = tmpLast.vl / MakeBook2.nRatio; + fwrite(&bk, sizeof(BookStruct), 1, MakeBook2.fpBookFile); + } +} + +static bool bInit = false; + +BOOL WINAPI MakeBook(LPCSTR szPath, LPCSTR szFile, + LONG nWin, LONG nDraw, LONG nLoss, LONG nRatio) { + BOOL bSuccess; + if (!bInit) { + bInit = true; + PreGenInit(); + ChineseInit(); + } + bSuccess = FALSE; + MakeBook2.nWin = nWin; + MakeBook2.nDraw = nDraw; + MakeBook2.nLoss = nLoss; + MakeBook2.nRatio = nRatio; + MakeBook2.fpTempFile = tmpfile(); + if (MakeBook2.fpTempFile != NULL) { + MakeBook2.nTempLen = 0; + MakeBook2.fpBookFile = fopen(szFile, "wb"); + if (MakeBook2.fpBookFile != NULL) { + SearchFolder(szPath); + if (MakeBook2.nTempLen > 1) { + MakeBook2.TempBuffer = new TempStruct[TEMP_BUFFER_SIZE]; + SortTemp(0, MakeBook2.nTempLen - 1); + delete[] MakeBook2.TempBuffer; + } + MakeBook(); + fclose(MakeBook2.fpBookFile); + bSuccess = TRUE; + } + fclose(MakeBook2.fpTempFile); + } + return bSuccess; +} + +LONG WINAPI GetBookMoves(const PositionStruct *lppos, + LPCSTR szFile, LPLONG lpMoves, LPLONG lpValues) { + int i, nMoves; + if (!bInit) { + bInit = true; + PreGenInit(); + ChineseInit(); + } + MoveStruct mvs[MAX_GEN_MOVES]; + nMoves = GetBookMoves(*lppos, szFile, mvs); + for (i = 0; i < nMoves; i ++) { + lpMoves[i] = mvs[i].wmv; + lpValues[i] = mvs[i].wvl; + } + return nMoves; +} \ No newline at end of file diff --git a/BOOK/MAKEBOOK.FRM b/BOOK/MAKEBOOK.FRM new file mode 100644 index 0000000..efe2e77 --- /dev/null +++ b/BOOK/MAKEBOOK.FRM @@ -0,0 +1,386 @@ +VERSION 5.00 +Object = "{FE0065C0-1B7B-11CF-9D53-00AA003C9CB6}#1.1#0"; "COMCT232.OCX" +Begin VB.Form frmMakeBook + BorderStyle = 1 'Fixed Single + Caption = "ElephantEye 开局库管理工具" + ClientHeight = 3195 + ClientLeft = 45 + ClientTop = 330 + ClientWidth = 4680 + BeginProperty Font + Name = "宋体" + Size = 9 + Charset = 0 + Weight = 400 + Underline = 0 'False + Italic = 0 'False + Strikethrough = 0 'False + EndProperty + Icon = "MAKEBOOK.frx":0000 + MaxButton = 0 'False + ScaleHeight = 3195 + ScaleWidth = 4680 + StartUpPosition = 3 'Windows Default + Begin VB.Frame frmMain + Caption = "文件和棋谱" + Height = 1215 + Left = 120 + TabIndex = 0 + Top = 120 + Width = 4455 + Begin VB.CommandButton btnPgnPath + Caption = "浏览" + Height = 375 + Left = 3600 + TabIndex = 6 + Top = 720 + Width = 735 + End + Begin VB.TextBox txtPgnPath + BackColor = &H8000000F& + Height = 270 + Left = 1080 + Locked = -1 'True + TabIndex = 5 + Top = 720 + Width = 2415 + End + Begin VB.CommandButton btnBookFile + Caption = "浏览" + Height = 375 + Left = 3600 + TabIndex = 3 + Top = 240 + Width = 735 + End + Begin VB.TextBox txtBookFile + BackColor = &H8000000F& + Height = 270 + Left = 1080 + Locked = -1 'True + TabIndex = 2 + Top = 240 + Width = 2415 + End + Begin VB.Label lblPgnPath + Caption = "棋谱文件夹" + Height = 255 + Left = 120 + TabIndex = 4 + Top = 720 + Width = 975 + End + Begin VB.Label lblBookFile + Caption = "开局库文件" + Height = 255 + Left = 120 + TabIndex = 1 + Top = 240 + Width = 975 + End + End + Begin VB.Frame frmOptions + Caption = "开局库参数" + Height = 1095 + Left = 120 + TabIndex = 7 + Top = 1440 + Width = 4455 + Begin ComCtl2.UpDown updnRatio + Height = 270 + Left = 2400 + TabIndex = 19 + Top = 720 + Width = 195 + _ExtentX = 344 + _ExtentY = 476 + _Version = 327681 + Value = 4 + AutoBuddy = -1 'True + BuddyControl = "txtRatio" + BuddyDispid = 196617 + OrigLeft = 2400 + OrigTop = 720 + OrigRight = 2595 + OrigBottom = 975 + Max = 9 + Min = 1 + SyncBuddy = -1 'True + BuddyProperty = 0 + Enabled = -1 'True + End + Begin ComCtl2.UpDown updnWin + Height = 270 + Left = 1680 + TabIndex = 11 + Top = 240 + Width = 195 + _ExtentX = 344 + _ExtentY = 476 + _Version = 327681 + Value = 3 + AutoBuddy = -1 'True + BuddyControl = "txtWin" + BuddyDispid = 196620 + OrigLeft = 1680 + OrigTop = 240 + OrigRight = 1875 + OrigBottom = 495 + Max = 5 + Min = -5 + SyncBuddy = -1 'True + BuddyProperty = 0 + Enabled = -1 'True + End + Begin ComCtl2.UpDown updnDraw + Height = 270 + Left = 2760 + TabIndex = 14 + Top = 240 + Width = 195 + _ExtentX = 344 + _ExtentY = 476 + _Version = 327681 + Value = 1 + AutoBuddy = -1 'True + BuddyControl = "txtDraw" + BuddyDispid = 196619 + OrigLeft = 2760 + OrigTop = 240 + OrigRight = 2955 + OrigBottom = 495 + Max = 5 + Min = -5 + SyncBuddy = -1 'True + BuddyProperty = 0 + Enabled = -1 'True + End + Begin ComCtl2.UpDown updnLoss + Height = 270 + Left = 3840 + TabIndex = 17 + Top = 240 + Width = 195 + _ExtentX = 344 + _ExtentY = 476 + _Version = 327681 + Value = -1 + AutoBuddy = -1 'True + BuddyControl = "txtLoss" + BuddyDispid = 196618 + OrigLeft = 3840 + OrigTop = 240 + OrigRight = 4035 + OrigBottom = 495 + Max = 5 + Min = -5 + SyncBuddy = -1 'True + BuddyProperty = 0 + Enabled = -1 'True + End + Begin VB.TextBox txtRatio + BackColor = &H8000000F& + Height = 270 + Left = 2160 + Locked = -1 'True + TabIndex = 18 + Text = "4" + Top = 720 + Width = 270 + End + Begin VB.TextBox txtLoss + BackColor = &H8000000F& + Height = 270 + Left = 3600 + Locked = -1 'True + TabIndex = 16 + Text = "-1" + Top = 240 + Width = 270 + End + Begin VB.TextBox txtDraw + BackColor = &H8000000F& + Height = 270 + Left = 2520 + Locked = -1 'True + TabIndex = 13 + Text = "1" + Top = 240 + Width = 270 + End + Begin VB.TextBox txtWin + BackColor = &H8000000F& + Height = 270 + Left = 1440 + Locked = -1 'True + TabIndex = 10 + Text = "3" + Top = 240 + Width = 270 + End + Begin VB.Label lblScore + Caption = "分数 =" + Height = 255 + Left = 240 + TabIndex = 8 + Top = 480 + Width = 615 + End + Begin VB.Line lnDivision + X1 = 840 + X2 = 4080 + Y1 = 600 + Y2 = 600 + End + Begin VB.Label lblLoss + Caption = "+负局x" + Height = 255 + Left = 3000 + TabIndex = 15 + Top = 240 + Width = 615 + End + Begin VB.Label lblDraw + Caption = "+和局x" + Height = 255 + Left = 1920 + TabIndex = 12 + Top = 240 + Width = 615 + End + Begin VB.Label lblWin + Caption = "胜局x" + Height = 255 + Left = 960 + TabIndex = 9 + Top = 240 + Width = 495 + End + End + Begin VB.CommandButton btnExit + Cancel = -1 'True + Caption = "退出" + Height = 375 + Left = 3240 + TabIndex = 22 + Top = 2640 + Width = 1095 + End + Begin VB.CommandButton btnViewBook + Caption = "浏览开局库" + Height = 375 + Left = 1800 + TabIndex = 21 + Top = 2640 + Width = 1215 + End + Begin VB.CommandButton btnMakeBook + Caption = "制作开局库" + Height = 375 + Left = 360 + TabIndex = 20 + Top = 2640 + Width = 1215 + End +End +Attribute VB_Name = "frmMakeBook" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False +' ElephantEye Book Maker - a Chinese Chess Book Maker Program for ElephantEye +' Designed by Morning Yellow, Version: 3.13, Last Modified: Mar. 2008 +' Copyright (C) 2004-2007 www.elephantbase.net +' +' This library is free software; you can redistribute it and/or +' modify it under the terms of the GNU Lesser General Public +' License as published by the Free Software Foundation; either +' version 2.1 of the License, or (at your option) any later version. +' +' This library 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 +' Lesser General Public License for more details. +' +' You should have received a copy of the GNU Lesser General Public +' License along with this library; if not, write to the Free Software +' Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Option Explicit + +Private Declare Function MakeBook Lib "MAKEBOOK.DLL" Alias "_MakeBook@24" (ByVal szPath As String, ByVal szFile As String, _ + ByVal nWin As Long, ByVal nDraw As Long, ByVal nLoss As Long, ByVal nRatio As Long) As Long + +Private fs As Object + +Private Sub Form_Load() + +CchessInit +EccoInitOpenVar +txtBookFile.Text = App.Path + IIf(Right(App.Path, 1) = "\", "", "\") + "BOOK.DAT" +Set fs = CreateObject("Scripting.FileSystemObject") + +End Sub + +Private Sub btnBookFile_Click() + +Dim sz As String +sz = SaveFileDialog("开局库文件", "开局库文件 (*.DAT)|*.DAT|所有文件 (*.*)|*.*", txtBookFile.Text) +If sz <> "" Then + txtBookFile.Text = sz +End If + +End Sub + +Private Sub btnPgnPath_Click() + +Dim sz As String +sz = BrowseForFolder("请选择棋谱所在的文件夹") +If sz <> "" Then + txtPgnPath.Text = sz +End If + +End Sub + +Private Sub btnMakeBook_Click() + +If txtBookFile.Text = vbNullString Then + MsgBox "没有指定开局库文件!", vbExclamation + Exit Sub +End If +If txtPgnPath.Text = vbNullString Then + MsgBox "没有指定棋谱文件夹!", vbExclamation + Exit Sub +End If +If fs.FileExists(txtBookFile.Text) Then + If MsgBox(txtBookFile.Text + " 已存在。" + vbCrLf + "要替换它吗?", vbExclamation + vbYesNo) <> vbYes Then + Exit Sub + End If +End If +If MakeBook(txtPgnPath.Text, txtBookFile.Text, Val(txtWin.Text), Val(txtDraw.Text), Val(txtLoss.Text), Val(txtRatio.Text)) Then + MsgBox "开局库制作成功!", vbInformation +Else + MsgBox "开局库制作失败!", vbExclamation +End If + +End Sub + +Private Sub btnViewBook_Click() + +If txtBookFile.Text = vbNullString Then + MsgBox "没有指定开局库文件!", vbExclamation + Exit Sub +End If +If fs.FileExists(txtBookFile.Text) Then + frmViewBook.Show vbModal, Me +Else + MsgBox "开局库文件 " + frmMakeBook.txtBookFile.Text + " 不存在!", vbExclamation +End If + +End Sub + +Private Sub btnExit_Click() + +Unload Me + +End Sub diff --git a/BOOK/MAKEBOOK.FRX b/BOOK/MAKEBOOK.FRX new file mode 100644 index 0000000..f9fa18b Binary files /dev/null and b/BOOK/MAKEBOOK.FRX differ diff --git a/BOOK/MAKEBOOK.VBP b/BOOK/MAKEBOOK.VBP new file mode 100644 index 0000000..a98a5e3 --- /dev/null +++ b/BOOK/MAKEBOOK.VBP @@ -0,0 +1,48 @@ +Type=Exe +Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\WINDOWS\system32\stdole2.tlb#OLE Automation +Object={831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0#0; MSCOMCTL.OCX +Object={FE0065C0-1B7B-11CF-9D53-00AA003C9CB6}#1.1#0; COMCT232.OCX +Module=mdlBase; ..\utility\BASE.BAS +Form=MAKEBOOK.FRM +Form=VIEWBOOK.FRM +Module=mdlCChess; ..\cchess\CCHESS.BAS +IconForm="frmMakeBook" +Startup="frmMakeBook" +HelpFile="" +Title="ElephantEye 开局库管理工具" +ExeName32="MAKEBOOK.EXE" +Path32="..\BIN" +Command32="" +Name="prjMakeBook" +HelpContextID="0" +CompatibleMode="0" +MajorVer=3 +MinorVer=13 +RevisionVer=0 +AutoIncrementVer=0 +ServerSupportFiles=0 +VersionComments="ElephantEye Book Maker 3.13, (C) 2004-2007 www.elephantbase.net" +VersionCompanyName="Author: Morning Yellow (Address: Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)" +VersionFileDescription="ElephantEye Book Maker" +VersionLegalCopyright="ElephantEye Book Maker 3.13, (C) 2004-2007 www.elephantbase.net" +VersionLegalTrademarks="www.elephantbase.net" +VersionProductName="ElephantEye Book Maker" +CompilationType=0 +OptimizationType=0 +FavorPentiumPro(tm)=0 +CodeViewDebugInfo=0 +NoAliasing=0 +BoundsCheck=0 +OverflowCheck=0 +FlPointCheck=0 +FDIVCheck=0 +UnroundedFP=0 +StartMode=0 +Unattended=0 +Retained=0 +ThreadPerObject=0 +MaxNumberOfThreads=1 +DebugStartupOption=0 + +[MS Transaction Server] +AutoRefresh=1 diff --git a/BOOK/MAKEFILE.BAT b/BOOK/MAKEFILE.BAT new file mode 100644 index 0000000..90a6f8a --- /dev/null +++ b/BOOK/MAKEFILE.BAT @@ -0,0 +1,7 @@ +@ECHO OFF +RC ..\RES\MAKEBOOK.RC +CL /DNDEBUG /O2 /W3 /LD /Fe..\BIN\MAKEBOOK.DLL ..\ELEEYE\PREGEN.CPP ..\ELEEYE\POSITION.CPP ..\ELEEYE\GENMOVES.CPP ..\ELEEYE\BOOK.CPP ..\CCHESS\CCHESS.CPP ..\CCHESS\PGNFILE.CPP MAKEBOOK.CPP ..\RES\MAKEBOOK.RES +DEL ..\RES\MAKEBOOK.RES +DEL *.OBJ +DEL ..\BIN\*.LIB +DEL ..\BIN\*.EXP \ No newline at end of file diff --git a/BOOK/RES/BA.ICO b/BOOK/RES/BA.ICO new file mode 100644 index 0000000..017c345 Binary files /dev/null and b/BOOK/RES/BA.ICO differ diff --git a/BOOK/RES/BB.ICO b/BOOK/RES/BB.ICO new file mode 100644 index 0000000..4d47a90 Binary files /dev/null and b/BOOK/RES/BB.ICO differ diff --git a/BOOK/RES/BC.ICO b/BOOK/RES/BC.ICO new file mode 100644 index 0000000..911aa6d Binary files /dev/null and b/BOOK/RES/BC.ICO differ diff --git a/BOOK/RES/BK.ICO b/BOOK/RES/BK.ICO new file mode 100644 index 0000000..88d64d2 Binary files /dev/null and b/BOOK/RES/BK.ICO differ diff --git a/BOOK/RES/BN.ICO b/BOOK/RES/BN.ICO new file mode 100644 index 0000000..c31873f Binary files /dev/null and b/BOOK/RES/BN.ICO differ diff --git a/BOOK/RES/BP.ICO b/BOOK/RES/BP.ICO new file mode 100644 index 0000000..c9798c8 Binary files /dev/null and b/BOOK/RES/BP.ICO differ diff --git a/BOOK/RES/BR.ICO b/BOOK/RES/BR.ICO new file mode 100644 index 0000000..7845e21 Binary files /dev/null and b/BOOK/RES/BR.ICO differ diff --git a/BOOK/RES/OO.ICO b/BOOK/RES/OO.ICO new file mode 100644 index 0000000..7845f62 Binary files /dev/null and b/BOOK/RES/OO.ICO differ diff --git a/BOOK/RES/RA.ICO b/BOOK/RES/RA.ICO new file mode 100644 index 0000000..f8d667b Binary files /dev/null and b/BOOK/RES/RA.ICO differ diff --git a/BOOK/RES/RB.ICO b/BOOK/RES/RB.ICO new file mode 100644 index 0000000..9d15c29 Binary files /dev/null and b/BOOK/RES/RB.ICO differ diff --git a/BOOK/RES/RC.ICO b/BOOK/RES/RC.ICO new file mode 100644 index 0000000..5a60969 Binary files /dev/null and b/BOOK/RES/RC.ICO differ diff --git a/BOOK/RES/RK.ICO b/BOOK/RES/RK.ICO new file mode 100644 index 0000000..bc4146c Binary files /dev/null and b/BOOK/RES/RK.ICO differ diff --git a/BOOK/RES/RN.ICO b/BOOK/RES/RN.ICO new file mode 100644 index 0000000..2b337fa Binary files /dev/null and b/BOOK/RES/RN.ICO differ diff --git a/BOOK/RES/RP.ICO b/BOOK/RES/RP.ICO new file mode 100644 index 0000000..0879582 Binary files /dev/null and b/BOOK/RES/RP.ICO differ diff --git a/BOOK/RES/RR.ICO b/BOOK/RES/RR.ICO new file mode 100644 index 0000000..db770d0 Binary files /dev/null and b/BOOK/RES/RR.ICO differ diff --git a/BOOK/VIEWBOOK.FRM b/BOOK/VIEWBOOK.FRM new file mode 100644 index 0000000..a29f405 --- /dev/null +++ b/BOOK/VIEWBOOK.FRM @@ -0,0 +1,918 @@ +VERSION 5.00 +Object = "{831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0#0"; "MSCOMCTL.OCX" +Begin VB.Form frmViewBook + Caption = "浏览开局库" + ClientHeight = 3195 + ClientLeft = 60 + ClientTop = 345 + ClientWidth = 4680 + BeginProperty Font + Name = "宋体" + Size = 9 + Charset = 0 + Weight = 400 + Underline = 0 'False + Italic = 0 'False + Strikethrough = 0 'False + EndProperty + Icon = "VIEWBOOK.frx":0000 + MinButton = 0 'False + ScaleHeight = 3195 + ScaleWidth = 4680 + StartUpPosition = 3 'Windows Default + Begin VB.CommandButton btnExit + Cancel = -1 'True + Caption = "关闭" + Height = 375 + Left = 600 + TabIndex = 0 + Top = 2760 + Width = 1215 + End + Begin MSComctlLib.ImageList imglst + Left = 0 + Top = 0 + _ExtentX = 1005 + _ExtentY = 1005 + BackColor = -2147483643 + ImageWidth = 16 + ImageHeight = 16 + MaskColor = 12632256 + _Version = 393216 + BeginProperty Images {2C247F25-8591-11D1-B16A-00C0F0283628} + NumListImages = 15 + BeginProperty ListImage1 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "VIEWBOOK.frx":0ABA + Key = "" + EndProperty + BeginProperty ListImage2 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "VIEWBOOK.frx":0C14 + Key = "" + EndProperty + BeginProperty ListImage3 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "VIEWBOOK.frx":0D6E + Key = "" + EndProperty + BeginProperty ListImage4 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "VIEWBOOK.frx":0EC8 + Key = "" + EndProperty + BeginProperty ListImage5 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "VIEWBOOK.frx":1022 + Key = "" + EndProperty + BeginProperty ListImage6 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "VIEWBOOK.frx":117C + Key = "" + EndProperty + BeginProperty ListImage7 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "VIEWBOOK.frx":12D6 + Key = "" + EndProperty + BeginProperty ListImage8 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "VIEWBOOK.frx":1430 + Key = "" + EndProperty + BeginProperty ListImage9 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "VIEWBOOK.frx":158A + Key = "" + EndProperty + BeginProperty ListImage10 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "VIEWBOOK.frx":16E4 + Key = "" + EndProperty + BeginProperty ListImage11 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "VIEWBOOK.frx":183E + Key = "" + EndProperty + BeginProperty ListImage12 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "VIEWBOOK.frx":1998 + Key = "" + EndProperty + BeginProperty ListImage13 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "VIEWBOOK.frx":1AF2 + Key = "" + EndProperty + BeginProperty ListImage14 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "VIEWBOOK.frx":1C4C + Key = "" + EndProperty + BeginProperty ListImage15 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "VIEWBOOK.frx":1DA6 + Key = "" + EndProperty + EndProperty + End + Begin MSComctlLib.TreeView tree + Height = 3135 + Left = 2400 + TabIndex = 1 + Top = 0 + Width = 2175 + _ExtentX = 3836 + _ExtentY = 5530 + _Version = 393217 + Indentation = 212 + LabelEdit = 1 + LineStyle = 1 + Style = 7 + ImageList = "imglst" + Appearance = 1 + BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851} + Name = "宋体" + Size = 9 + Charset = 134 + Weight = 400 + Underline = 0 'False + Italic = 0 'False + Strikethrough = 0 'False + EndProperty + End + Begin VB.Image imgSquares + Height = 255 + Index = 89 + Left = 2040 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 88 + Left = 1800 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 87 + Left = 1560 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 86 + Left = 1320 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 85 + Left = 1080 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 84 + Left = 840 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 83 + Left = 600 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 82 + Left = 360 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 81 + Left = 120 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 80 + Left = 2040 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 79 + Left = 1800 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 78 + Left = 1560 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 77 + Left = 1320 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 76 + Left = 1080 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 75 + Left = 840 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 74 + Left = 600 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 73 + Left = 360 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 72 + Left = 120 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 71 + Left = 2040 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 70 + Left = 1800 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 69 + Left = 1560 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 68 + Left = 1320 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 67 + Left = 1080 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 66 + Left = 840 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 65 + Left = 600 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 64 + Left = 360 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 63 + Left = 120 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 62 + Left = 2040 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 61 + Left = 1800 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 60 + Left = 1560 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 59 + Left = 1320 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 58 + Left = 1080 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 57 + Left = 840 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 56 + Left = 600 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 55 + Left = 360 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 54 + Left = 120 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 53 + Left = 2040 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 52 + Left = 1800 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 51 + Left = 1560 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 50 + Left = 1320 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 49 + Left = 1080 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 48 + Left = 840 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 47 + Left = 600 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 46 + Left = 360 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 45 + Left = 120 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 44 + Left = 2040 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 43 + Left = 1800 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 42 + Left = 1560 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 41 + Left = 1320 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 40 + Left = 1080 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 39 + Left = 840 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 38 + Left = 600 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 37 + Left = 360 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 36 + Left = 120 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 35 + Left = 2040 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 34 + Left = 1800 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 33 + Left = 1560 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 32 + Left = 1320 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 31 + Left = 1080 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 30 + Left = 840 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 29 + Left = 600 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 28 + Left = 360 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 27 + Left = 120 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 26 + Left = 2040 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 25 + Left = 1800 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 24 + Left = 1560 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 23 + Left = 1320 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 22 + Left = 1080 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 21 + Left = 840 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 20 + Left = 600 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 19 + Left = 360 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 18 + Left = 120 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 17 + Left = 2040 + Top = 360 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 16 + Left = 1800 + Top = 360 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 15 + Left = 1560 + Top = 360 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 14 + Left = 1320 + Top = 360 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 13 + Left = 1080 + Top = 360 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 12 + Left = 840 + Top = 360 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 11 + Left = 600 + Top = 360 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 10 + Left = 360 + Top = 360 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 9 + Left = 120 + Top = 360 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 8 + Left = 2040 + Top = 120 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 7 + Left = 1800 + Top = 120 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 6 + Left = 1560 + Top = 120 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 5 + Left = 1320 + Top = 120 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 4 + Left = 1080 + Top = 120 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 3 + Left = 840 + Top = 120 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 2 + Left = 600 + Top = 120 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 1 + Left = 360 + Top = 120 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 0 + Left = 120 + Top = 120 + Width = 255 + End + Begin VB.Image imgBoard + Height = 2655 + Left = 0 + Picture = "VIEWBOOK.frx":1F00 + Stretch = -1 'True + Top = 0 + Width = 2415 + End +End +Attribute VB_Name = "frmViewBook" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False +' ElephantEye Book Maker - a Chinese Chess Book Maker Program for ElephantEye +' Designed by Morning Yellow, Version: 3.13, Last Modified: Mar. 2008 +' Copyright (C) 2004-2007 www.elephantbase.net +' +' This library is free software; you can redistribute it and/or +' modify it under the terms of the GNU Lesser General Public +' License as published by the Free Software Foundation; either +' version 2.1 of the License, or (at your option) any later version. +' +' This library 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 +' Lesser General Public License for more details. +' +' You should have received a copy of the GNU Lesser General Public +' License along with this library; if not, write to the Free Software +' Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Option Explicit + +Private Declare Function GetBookMoves Lib "MAKEBOOK.DLL" Alias "_GetBookMoves@16" (ByRef pos As PositionStruct, _ + ByVal szFile As String, ByRef nMoves As Long, ByRef nValues As Long) As Long + +Private Const MAX_NODE_NUM As Integer = 32767 +Private Nodes_szFen(0 To MAX_NODE_NUM) As String +Private Nodes_szFile(0 To MAX_NODE_NUM) As String +Private Nodes_bExpanded(0 To MAX_NODE_NUM) As Boolean +Private Nodes_nIndex As Integer + +Private Sub AddNode(ByRef pos As PositionStruct, ByVal mv As Long, ByVal vl As Long, Optional ByVal nIndex As Integer = -1) + +Dim nStatus As Long, sz As String, dwFile As Long +Dim pc As Integer, nPicId As Integer + +If Nodes_nIndex = MAX_NODE_NUM Then + Exit Sub +End If +pc = pos.ucpcSquares(Src(mv)) +If pc = 0 Then + nPicId = 1 +Else + nPicId = PieceType(pc) + IIf(pc < 32, 2, 9) +End If +dwFile = CchessMove2File(mv, pos) +sz = Mid(MkC(CchessFile2Chin(dwFile, pos.sdPlayer)), 2) + "(" + LTrim(Str(vl)) + ")" +If nIndex = -1 Then + tree.Nodes.Add , , , sz, nPicId +Else + tree.Nodes.Add nIndex + 1, tvwChild, , sz, nPicId +End If +CchessTryMove pos, nStatus, mv +Nodes_szFen(Nodes_nIndex) = MkBStr(CchessBoard2Fen(pos)) +If nIndex = -1 Then + Nodes_szFile(Nodes_nIndex) = MkL(dwFile) +Else + Nodes_szFile(Nodes_nIndex) = Nodes_szFile(nIndex) + MkL(dwFile) +End If +CchessUndoMove pos +Nodes_bExpanded(Nodes_nIndex) = False +Nodes_nIndex = Nodes_nIndex + 1 + +End Sub + +Private Sub DrawPos(ByRef pos As PositionStruct) + +Dim i As Integer, j As Integer, pc As Integer, nPicId As Integer +For i = 3 To 12 + For j = 3 To 11 + pc = pos.ucpcSquares(i * 16 + j) + If pc = 0 Then + nPicId = 1 + Else + nPicId = PieceType(pc) + IIf(pc < 32, 2, 9) + End If + imgSquares((i - 3) * 9 + j - 3).Picture = imglst.ListImages.Item(nPicId).Picture + Next +Next + +End Sub + +Private Sub Form_Load() + +Dim i As Integer, nMoveNum As Integer +Dim mvs(1 To 128) As Long, vls(1 To 128) As Long +Dim pos As PositionStruct + +tree.Nodes.Clear +Nodes_nIndex = 0 +CchessFen2Board pos, CCHESS_START_FEN +DrawPos pos +nMoveNum = GetBookMoves(pos, frmMakeBook.txtBookFile.Text, mvs(1), vls(1)) +If nMoveNum = 0 Then + MsgBox "开局库文件 " + frmMakeBook.txtBookFile.Text + " 格式不正确!", vbExclamation +End If +For i = 1 To nMoveNum + AddNode pos, mvs(i), vls(i) +Next + +End Sub + +Private Sub form_Resize() + +If Height < 3600 Then + Height = 3600 +End If +If Width < 4800 Then + Width = 4800 +End If +tree.Height = Height - 420 +tree.Width = Width - 2520 + +End Sub + +Private Sub tree_NodeClick(ByVal n As Node) + +Dim i As Integer, nMoveNum, nIndex As Integer +Dim dwEcco As Long, szEcco As String, szOpen As String, szVar As String +Dim mvs(1 To 128) As Long, vls(1 To 128) As Long +Dim pos As PositionStruct + +nIndex = n.Index - 1 +CchessFen2Board pos, Nodes_szFen(nIndex) +DrawPos pos +If Not Nodes_bExpanded(nIndex) Then + Nodes_bExpanded(nIndex) = True + nMoveNum = GetBookMoves(pos, frmMakeBook.txtBookFile.Text, mvs(1), vls(1)) + For i = 1 To nMoveNum + AddNode pos, mvs(i), vls(i), nIndex + Next +End If +dwEcco = EccoIndex(Nodes_szFile(nIndex)) +szEcco = Left(MkL(dwEcco), 3) +szOpen = MkBStr(EccoOpening(dwEcco)) +szVar = MkBStr(EccoVariation(dwEcco)) + +Caption = "浏览开局库 - " + szOpen + IIf(szVar = "", "", "——") + szVar + "(" + szEcco + ")" + +End Sub + +Private Sub btnExit_Click() + +Unload Me + +End Sub diff --git a/BOOK/VIEWBOOK.FRX b/BOOK/VIEWBOOK.FRX new file mode 100644 index 0000000..904294b Binary files /dev/null and b/BOOK/VIEWBOOK.FRX differ diff --git "a/CCGC/01.\345\256\235\345\262\233\344\270\200\345\217\267(\345\205\210\350\264\237)\347\233\270\347\234\274\347\253\236\346\212\200(A1-A2).PGN" "b/CCGC/01.\345\256\235\345\262\233\344\270\200\345\217\267(\345\205\210\350\264\237)\347\233\270\347\234\274\347\253\236\346\212\200(A1-A2).PGN" new file mode 100644 index 0000000..5811e42 --- /dev/null +++ "b/CCGC/01.\345\256\235\345\262\233\344\270\200\345\217\267(\345\205\210\350\264\237)\347\233\270\347\234\274\347\253\236\346\212\200(A1-A2).PGN" @@ -0,0 +1,31 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "1"] +[Date "2006.08.03"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "宝岛一号"] +[BlackTeam ""] +[Black "相眼竞技"] +[Result "0-1"] +[ECCO "C48"] +[Opening "中炮过河车互进七兵对屏风马平炮兑车"] +[Variation "红仕角炮对黑退边炮"] + 1. 炮八平五 马2进3 + 2. 马八进七 车1平2 + 3. 车九平八 马8进7 + 4. 兵三进一 卒3进1 + 5. 车八进六 炮2平1 + 6. 车八平七 炮1退1 + 7. 炮二平四 车2进5 + 8. 马二进三 车2平7 + 9. 车一平二 车9平8 + 10. 车二进三 士6进5 + 11. 兵五进一 车7退1 + 12. 马三进四 炮1平3 + 13. 车七平六 车7进1 + 0-1 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/02.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\345\222\214)\345\256\235\345\262\233\344\270\200\345\217\267(A2-A1).PGN" "b/CCGC/02.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\345\222\214)\345\256\235\345\262\233\344\270\200\345\217\267(A2-A1).PGN" new file mode 100644 index 0000000..56c1c91 --- /dev/null +++ "b/CCGC/02.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\345\222\214)\345\256\235\345\262\233\344\270\200\345\217\267(A2-A1).PGN" @@ -0,0 +1,48 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "2"] +[Date "2006.08.03"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "相眼竞技"] +[BlackTeam ""] +[Black "宝岛一号"] +[Result "1/2-1/2"] +[ECCO "C47"] +[Opening "中炮过河车互进七兵对屏风马平炮兑车"] +[Variation "红左边马对黑退边炮"] + 1. 炮二平五 马8进7 + 2. 马二进三 车9平8 + 3. 车一平二 卒7进1 + 4. 车二进六 马2进3 + 5. 兵七进一 炮8平9 + 6. 车二平三 炮9退1 + 7. 马八进九 车8进5 + 8. 兵五进一 马3退5 + 9. 炮八进四 炮2平5 + 10. 马九进七 炮9平7 + 11. 车三平四 马5进3 + 12. 车九进一 卒7进1 + 13. 车九平四 卒7平6 + 14. 兵三进一 车8平7 + 15. 后车进三 车7平6 + 16. 马三进四 车1平2 + 17. 车四进一 车2进3 + 18. 车四平三 车2进3 + 19. 车三进一 车2平3 + 20. 车三进一 炮5进3 + 21. 仕四进五 士4进5 + 22. 车三退三 车3平5 + 23. 车三平一 炮5进2 + 24. 相七进五 车5退2 + 25. 兵一进一 车5平6 + 26. 马四退六 象3进5 + 27. 车一进一 马3退4 + 28. 车一退二 象5进7 + 29. 马六退四 卒3进1 + 30. 兵七进一 车6平3 + 31. 马四进五 1/2-1/2 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/05.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\264\237)\350\261\241\346\243\213\346\227\213\351\243\216(A2-A6).PGN" "b/CCGC/05.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\264\237)\350\261\241\346\243\213\346\227\213\351\243\216(A2-A6).PGN" new file mode 100644 index 0000000..27a59ed --- /dev/null +++ "b/CCGC/05.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\264\237)\350\261\241\346\243\213\346\227\213\351\243\216(A2-A6).PGN" @@ -0,0 +1,76 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "5"] +[Date "2006.08.03"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "相眼竞技"] +[BlackTeam ""] +[Black "象棋旋风"] +[Result "0-1"] +[ECCO "E11"] +[Opening "仙人指路飞相对卒底炮"] +[Variation ""] + 1. 兵七进一 炮2平3 + 2. 相三进五 马2进1 + 3. 马八进七 车1平2 + 4. 马七进六 炮8平5 + 5. 车九进一 马8进7 + 6. 车九平四 车9平8 + 7. 仕四进五 卒7进1 + 8. 车四进五 车8进5 + 9. 马六进五 马7进5 + 10. 车四平五 卒3进1 + 11. 兵七进一 炮3进7 + 12. 相五退七 车2进7 + 13. 马二进四 车2平3 + 14. 兵七平六 车8平4 + 15. 兵六进一 士4进5 + 16. 车一平三 炮5平2 + 17. 兵六进一 炮2进7 + 18. 车五平八 车3进2 + 19. 仕五进六 马1进3 + 20. 车八退五 车4平2 + 21. 车八退一 车3平2 + 22. 兵六平七 前车平3 + 23. 车三平二 马3进1 + 24. 炮二进二 车2平6 + 25. 兵九进一 车6进3 + 26. 兵九进一 卒1进1 + 27. 兵七平八 车6退2 + 28. 仕六退五 车6平5 + 29. 炮二平四 车5平7 + 30. 车二进六 车3退4 + 31. 炮四退二 车7平9 + 32. 车二平三 象7进5 + 33. 炮四平二 车9平8 + 34. 炮二平六 卒9进1 + 35. 炮六平八 车3平2 + 36. 炮八平六 车2退3 + 37. 车三平一 车2进5 + 38. 炮六平四 车8退2 + 39. 车一平五 车8进5 + 40. 炮四退二 车2退2 + 41. 车五平九 卒1进1 + 42. 车九平六 卒9进1 + 43. 车六平一 卒1进1 + 44. 车一平九 卒1平2 + 45. 仕五进四 卒9平8 + 46. 车九平六 卒7进1 + 47. 仕四退五 卒7进1 + 48. 车六平三 卒8平7 + 49. 车三平一 前卒进1 + 50. 车一退四 前卒进1 + 51. 车一退一 车8平7 + 52. 仕五进四 卒2平3 + 53. 车一平三 车7退1 + 54. 仕六进五 车7进1 + 55. 帅五平六 车2进4 + 56. 帅六进一 卒3进1 + 57. 仕五进六 车2退1 + 58. 帅六退一 车7平6 + 0-1 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/06.\350\261\241\346\243\213\346\227\213\351\243\216(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(A6-A2).PGN" "b/CCGC/06.\350\261\241\346\243\213\346\227\213\351\243\216(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(A6-A2).PGN" new file mode 100644 index 0000000..2b5d477 --- /dev/null +++ "b/CCGC/06.\350\261\241\346\243\213\346\227\213\351\243\216(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(A6-A2).PGN" @@ -0,0 +1,79 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "6"] +[Date "2006.08.03"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "象棋旋风"] +[BlackTeam ""] +[Black "相眼竞技"] +[Result "1-0"] +[ECCO "C49"] +[Opening "中炮过河车互进七兵对屏风马平炮兑车"] +[Variation "红进中兵对黑退边炮"] + 1. 炮二平五 马2进3 + 2. 马二进三 马8进7 + 3. 车一平二 车9平8 + 4. 兵七进一 卒7进1 + 5. 车二进六 炮8平9 + 6. 车二平三 炮9退1 + 7. 兵五进一 士4进5 + 8. 兵五进一 炮9平7 + 9. 车三平四 卒7进1 + 10. 马三进五 车8进8 + 11. 马八进七 卒7进1 + 12. 马五进六 象3进5 + 13. 马六进七 车1平3 + 14. 后马退五 车3进2 + 15. 兵五进一 卒3进1 + 16. 炮八平七 炮2进4 + 17. 炮七进一 马7进8 + 18. 车四平三 马8进6 + 19. 车三进二 马6进4 + 20. 炮五进五 车3平5 + 21. 马五进六 车5进1 + 22. 仕六进五 炮2平4 + 23. 兵七进一 卒7平6 + 24. 炮七平四 炮4平5 + 25. 相三进五 车8平6 + 26. 炮四进五 车6退4 + 27. 车九平八 车6平3 + 28. 车三进一 车5平2 + 29. 车三退四 车3进3 + 30. 炮四平二 士5进4 + 31. 炮二进一 将5进1 + 32. 车三进三 将5进1 + 33. 车三退五 炮5退2 + 34. 车三平五 车2平5 + 35. 车五进一 车3退4 + 36. 炮二退六 士6进5 + 37. 车五平三 炮5平8 + 38. 车三进三 士5进6 + 39. 车三退二 炮8退1 + 40. 车八进三 车5进2 + 41. 炮二平五 炮8平5 + 42. 车三退一 车5退1 + 43. 车三进二 士4退5 + 44. 车三平一 卒1进1 + 45. 兵一进一 车3平4 + 46. 车八平六 车4平2 + 47. 车一平三 卒1进1 + 48. 帅五平六 车5进2 + 49. 车六平五 车2平4 + 50. 帅六平五 将5平4 + 51. 车三平五 车4平5 + 52. 车五进三 将4退1 + 53. 车五平六 士5进4 + 54. 兵九进一 士6退5 + 55. 车六平七 将4退1 + 56. 车七进三 将4进1 + 57. 帅五平六 士5退6 + 58. 车七平四 将4平5 + 59. 车四退二 将5退1 + 60. 车四平六 将5进1 + 61. 车六进一 将5退1 + 62. 车六平四 1-0 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/07.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\203\234)\346\243\213\344\271\220\346\227\240\347\251\267(A2-A4).PGN" "b/CCGC/07.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\203\234)\346\243\213\344\271\220\346\227\240\347\251\267(A2-A4).PGN" new file mode 100644 index 0000000..067e89c --- /dev/null +++ "b/CCGC/07.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\203\234)\346\243\213\344\271\220\346\227\240\347\251\267(A2-A4).PGN" @@ -0,0 +1,70 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "7"] +[Date "2006.08.03"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "相眼竞技"] +[BlackTeam ""] +[Black "棋乐无穷"] +[Result "1-0"] +[ECCO "A02"] +[Opening "边马局"] +[Variation ""] + 1. 马二进一 卒3进1 + 2. 相七进五 马2进3 + 3. 车一进一 马8进7 + 4. 兵七进一 卒3进1 + 5. 车一平七 马3进4 + 6. 车七进三 马4进5 + 7. 车七进四 士4进5 + 8. 马八进七 马5退6 + 9. 仕六进五 炮2进2 + 10. 车七退三 炮2平1 + 11. 车九平六 卒7进1 + 12. 兵九进一 象3进5 + 13. 车七平八 马6进7 + 14. 兵九进一 炮8进2 + 15. 车八退一 卒1进1 + 16. 马七进五 车1平3 + 17. 炮八平七 车9进1 + 18. 马五进七 车3平4 + 19. 炮二平三 前马退6 + 20. 马七进八 车4进9 + 21. 仕五退六 士5进6 + 22. 马八退六 车9平3 + 23. 马六进四 炮8退3 + 24. 相五进七 车3平4 + 25. 炮三进五 马6进5 + 26. 炮七退一 马5退4 + 27. 车八进五 车4退1 + 28. 车八平六 将5平4 + 29. 马四进二 炮8平9 + 30. 相七退五 将4平5 + 31. 炮七平五 将5平4 + 32. 炮三进一 炮9进5 + 33. 炮三平四 象7进9 + 34. 马一进三 马4进2 + 35. 马三进四 象5进3 + 36. 炮四平一 士6进5 + 37. 马四退六 卒5进1 + 38. 炮五平六 炮9平4 + 39. 马六进八 炮4平3 + 40. 相五进七 象3退5 + 41. 炮六进三 炮3进3 + 42. 仕六进五 马2进3 + 43. 炮六退二 卒5进1 + 44. 炮六进三 马3退5 + 45. 马八进六 士5进4 + 46. 马二进四 士6退5 + 47. 炮一平五 将4平5 + 48. 炮五退四 士4退5 + 49. 马四退三 马5进3 + 50. 马三进二 马3进4 + 51. 帅五平六 炮3平6 + 52. 马二退四 将5平6 + 53. 炮六平四 1-0 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/08.\346\243\213\344\271\220\346\227\240\347\251\267(\345\205\210\350\264\237)\347\233\270\347\234\274\347\253\236\346\212\200(A4-A2).PGN" "b/CCGC/08.\346\243\213\344\271\220\346\227\240\347\251\267(\345\205\210\350\264\237)\347\233\270\347\234\274\347\253\236\346\212\200(A4-A2).PGN" new file mode 100644 index 0000000..31b49ef --- /dev/null +++ "b/CCGC/08.\346\243\213\344\271\220\346\227\240\347\251\267(\345\205\210\350\264\237)\347\233\270\347\234\274\347\253\236\346\212\200(A4-A2).PGN" @@ -0,0 +1,51 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "8"] +[Date "2006.08.03"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "棋乐无穷"] +[BlackTeam ""] +[Black "相眼竞技"] +[Result "0-1"] +[ECCO "C42"] +[Opening "中炮过河车互进七兵对屏风马平炮兑车"] +[Variation "红七路马对黑退边炮"] + 1. 炮二平五 马8进7 + 2. 马二进三 车9平8 + 3. 车一平二 马2进3 + 4. 兵七进一 卒7进1 + 5. 车二进六 炮8平9 + 6. 车二平三 炮9退1 + 7. 马八进七 车1进1 + 8. 炮八平九 车1平6 + 9. 车三退一 炮2平1 + 10. 车九进一 车6进1 + 11. 兵三进一 卒3进1 + 12. 车三平七 炮9平3 + 13. 兵三进一 车8进3 + 14. 车九平七 炮3进3 + 15. 兵七进一 象3进5 + 16. 兵七进一 马3退2 + 17. 马七进六 象5进7 + 18. 炮五进四 车6平4 + 19. 炮五平九 车8进3 + 20. 车七进三 车8平7 + 21. 相七进五 炮1进4 + 22. 后炮平六 车4平1 + 23. 炮六平九 车1平2 + 24. 前炮平八 马2进4 + 25. 兵一进一 象7退5 + 26. 车七平八 炮1退3 + 27. 炮八退一 马7进5 + 28. 炮九平八 炮1进6 + 29. 仕六进五 马5进4 + 30. 前炮平五 士4进5 + 31. 车八平六 车2进5 + 32. 帅五平六 车2退6 + 33. 仕五进六 马4进3 + 0-1 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/09.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\203\234)\347\220\206\346\262\273\346\243\213\345\243\256(A2-A3).PGN" "b/CCGC/09.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\203\234)\347\220\206\346\262\273\346\243\213\345\243\256(A2-A3).PGN" new file mode 100644 index 0000000..3bd072f --- /dev/null +++ "b/CCGC/09.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\203\234)\347\220\206\346\262\273\346\243\213\345\243\256(A2-A3).PGN" @@ -0,0 +1,60 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "9"] +[Date "2006.08.04"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "相眼竞技"] +[BlackTeam ""] +[Black "理治棋壮"] +[Result "1-0"] +[ECCO "E01"] +[Opening "仙人指路对飞象"] +[Variation ""] + 1. 兵三进一 象7进5 + 2. 炮二平四 卒3进1 + 3. 马二进三 马2进3 + 4. 车一平二 马8进6 + 5. 炮八平五 马3进4 + 6. 车九进一 马4进3 + 7. 马八进九 马3进5 + 8. 相七进五 炮2平1 + 9. 马三进四 车1进1 + 10. 车九平六 炮1进4 + 11. 车六进二 炮8平9 + 12. 车六平九 炮9进4 + 13. 车二进三 炮9退1 + 14. 车二进一 炮9退1 + 15. 车九平六 车9平7 + 16. 车二进四 炮9进1 + 17. 兵三进一 卒3进1 + 18. 兵三进一 卒3平4 + 19. 车六退二 卒4平5 + 20. 兵五进一 炮9平5 + 21. 仕四进五 马6进7 + 22. 车二平九 马7进6 + 23. 帅五平四 车7进6 + 24. 车九退二 马6进7 + 25. 帅四平五 车7平6 + 26. 车九平五 炮5进1 + 27. 车六进七 士4进5 + 28. 车五进一 卒9进1 + 29. 车五平七 象3进1 + 30. 车七平九 士5退4 + 31. 车九平五 士6进5 + 32. 车六退四 车6进1 + 33. 车五退四 车6进2 + 34. 仕五退四 马7退5 + 35. 马九进七 马5退6 + 36. 车六平三 马6退8 + 37. 车三进五 士5退6 + 38. 车三退三 马8进9 + 39. 马七进六 马9进8 + 40. 马六进八 士4进5 + 41. 马八进七 将5平4 + 42. 车三平六 士5进4 + 43. 车六进一 1-0 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/10.\347\220\206\346\262\273\346\243\213\345\243\256(\345\205\210\345\222\214)\347\233\270\347\234\274\347\253\236\346\212\200(A3-A2).PGN" "b/CCGC/10.\347\220\206\346\262\273\346\243\213\345\243\256(\345\205\210\345\222\214)\347\233\270\347\234\274\347\253\236\346\212\200(A3-A2).PGN" new file mode 100644 index 0000000..510a8d9 --- /dev/null +++ "b/CCGC/10.\347\220\206\346\262\273\346\243\213\345\243\256(\345\205\210\345\222\214)\347\233\270\347\234\274\347\253\236\346\212\200(A3-A2).PGN" @@ -0,0 +1,63 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "10"] +[Date "2006.08.04"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "理治棋壮"] +[BlackTeam ""] +[Black "相眼竞技"] +[Result "1/2-1/2"] +[ECCO "A63"] +[Opening "过宫炮对左中炮"] +[Variation ""] + 1. 炮八平四 炮2平5 + 2. 马八进七 马2进3 + 3. 仕六进五 车1平2 + 4. 相七进五 马8进7 + 5. 车九平六 车2进4 + 6. 炮二平三 卒7进1 + 7. 兵三进一 马7进6 + 8. 兵三进一 马6进5 + 9. 兵三进一 车9平8 + 10. 马七进五 炮5进4 + 11. 兵三进一 炮8进4 + 12. 兵三进一 车8进4 + 13. 炮三进七 士6进5 + 14. 马二进三 车8平6 + 15. 车一平二 炮8平7 + 16. 炮三退六 车2平5 + 17. 车二进九 车6退4 + 18. 车二平四 士5退6 + 19. 炮四进四 卒3进1 + 20. 兵三平四 士6进5 + 21. 炮三平四 车5平6 + 22. 马三进五 车6进2 + 23. 兵七进一 卒3进1 + 24. 炮四平三 车6平5 + 25. 车六进六 车5平7 + 26. 炮三退二 马3进2 + 27. 车六平五 车7平6 + 28. 炮三平五 车6退5 + 29. 车五平一 士5进4 + 30. 相五进七 马2进1 + 31. 车一平五 将5平6 + 32. 车五平九 马1退3 + 33. 车九平一 车6进4 + 34. 车一进三 将6进1 + 35. 炮五退二 士4退5 + 36. 车一退五 车6平9 + 37. 兵一进一 马3退1 + 38. 兵一进一 马1进2 + 39. 炮五进二 马2进3 + 40. 帅五平六 将6进1 + 41. 兵一平二 将6平5 + 42. 兵二平三 马3退4 + 43. 兵三进一 象3进1 + 44. 兵三进一 马4退6 + 45. 兵三进一 马6退7 + 1/2-1/2 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/11.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\203\234)\350\261\241\346\243\213ABC(D2-E3).PGN" "b/CCGC/11.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\203\234)\350\261\241\346\243\213ABC(D2-E3).PGN" new file mode 100644 index 0000000..b4175ec --- /dev/null +++ "b/CCGC/11.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\203\234)\350\261\241\346\243\213ABC(D2-E3).PGN" @@ -0,0 +1,62 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "11"] +[Date "2006.08.06"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "相眼竞技"] +[BlackTeam ""] +[Black "象棋ABC"] +[Result "1-0"] +[ECCO "C93"] +[Opening "五八炮互进三兵对屏风马"] +[Variation "红左边马"] + 1. 炮二平五 马2进3 + 2. 马二进三 马8进7 + 3. 车一平二 车9平8 + 4. 兵三进一 卒3进1 + 5. 马八进九 车1进1 + 6. 炮八进四 马3进4 + 7. 车九进一 炮2平5 + 8. 车二进三 车1平2 + 9. 炮八平三 车2进6 + 10. 兵三进一 马4进3 + 11. 炮三平九 马3进5 + 12. 相三进五 炮8退1 + 13. 兵三进一 炮8平3 + 14. 车二平四 马7退5 + 15. 车九平四 象7进9 + 16. 炮九进三 炮5平1 + 17. 前车进四 炮3进1 + 18. 前车退三 炮3进7 + 19. 相五退七 车2平7 + 20. 前车进三 炮1进5 + 21. 相七进九 车7平4 + 22. 前车平一 车4退1 + 23. 仕六进五 马5进4 + 24. 车四进四 马4退3 + 25. 炮九平八 车8平7 + 26. 车一平七 车7进1 + 27. 车四平七 车4平2 + 28. 炮八平六 将5平4 + 29. 后车平六 将4平5 + 30. 兵三进一 车7平6 + 31. 兵三平四 车2进3 + 32. 相九退七 车6进1 + 33. 车七平四 车2平3 + 34. 仕五退六 马3进5 + 35. 车四平一 车3退6 + 36. 兵九进一 卒9进1 + 37. 车一平三 车3退2 + 38. 兵九进一 士6进5 + 39. 兵九平八 车3进6 + 40. 车六平一 车3退1 + 41. 仕四进五 士5退6 + 42. 兵一进一 士6进5 + 43. 兵八进一 士5退6 + 44. 兵五进一 车3退1 + 45. 车一平六 1-0 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/12.\350\261\241\346\243\213ABC(\345\205\210\350\264\237)\347\233\270\347\234\274\347\253\236\346\212\200(E3-D2).PGN" "b/CCGC/12.\350\261\241\346\243\213ABC(\345\205\210\350\264\237)\347\233\270\347\234\274\347\253\236\346\212\200(E3-D2).PGN" new file mode 100644 index 0000000..644d4d1 --- /dev/null +++ "b/CCGC/12.\350\261\241\346\243\213ABC(\345\205\210\350\264\237)\347\233\270\347\234\274\347\253\236\346\212\200(E3-D2).PGN" @@ -0,0 +1,47 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "12"] +[Date "2006.08.06"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "象棋ABC"] +[BlackTeam ""] +[Black "相眼竞技"] +[Result "0-1"] +[ECCO "A31"] +[Opening "飞相进右马对左过宫炮"] +[Variation ""] + 1. 相七进五 炮2平6 + 2. 马八进七 马2进3 + 3. 车九平八 车1平2 + 4. 炮八进四 卒3进1 + 5. 炮八平七 象7进5 + 6. 车八进九 马3退2 + 7. 马二进一 卒9进1 + 8. 车一进一 卒7进1 + 9. 兵七进一 卒9进1 + 10. 车一平四 马8进7 + 11. 兵一进一 卒3进1 + 12. 相五进七 马2进1 + 13. 炮七平八 车9进5 + 14. 相七退五 炮8平9 + 15. 炮二退一 炮9进5 + 16. 车四进六 马7进8 + 17. 车四退五 炮9进2 + 18. 炮二平五 马8进7 + 19. 相五退七 车9进2 + 20. 炮五进五 士6进5 + 21. 车四平一 马7进9 + 22. 炮五平四 卒7进1 + 23. 马七进六 马9进7 + 24. 帅五进一 炮9退4 + 25. 炮四退五 炮9平4 + 26. 帅五退一 卒7平6 + 27. 炮八退五 炮4进3 + 28. 仕四进五 炮4平6 + 29. 兵五进一 炮6平2 + 0-1 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/13.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\264\237)\345\260\206\347\245\236\344\274\240\350\257\264(D2-F3).PGN" "b/CCGC/13.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\264\237)\345\260\206\347\245\236\344\274\240\350\257\264(D2-F3).PGN" new file mode 100644 index 0000000..79c2b23 --- /dev/null +++ "b/CCGC/13.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\264\237)\345\260\206\347\245\236\344\274\240\350\257\264(D2-F3).PGN" @@ -0,0 +1,105 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "13"] +[Date "2006.08.06"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "相眼竞技"] +[BlackTeam ""] +[Black "将神传说"] +[Result "0-1"] +[ECCO "C16"] +[Opening "中炮巡河车对屏风马"] +[Variation "红进左马"] + 1. 炮八平五 马2进3 + 2. 马八进七 车1平2 + 3. 兵三进一 卒3进1 + 4. 马二进三 马8进7 + 5. 车九平八 炮8进4 + 6. 车八进四 炮2平1 + 7. 车八进五 马3退2 + 8. 马三进四 炮8平3 + 9. 炮二进四 炮3进3 + 10. 仕六进五 士6进5 + 11. 炮五进四 象7进5 + 12. 炮五退一 卒3进1 + 13. 相三进五 炮3退1 + 14. 相五进七 车9平8 + 15. 车一平二 炮3退3 + 16. 马四进三 马2进3 + 17. 兵三进一 炮3进1 + 18. 兵三平四 马3进4 + 19. 兵五进一 马4进2 + 20. 马七进五 炮1进4 + 21. 马五退六 炮3进2 + 22. 车二进二 炮1进3 + 23. 炮二退二 炮3进1 + 24. 马六退八 马2进4 + 25. 车二平七 车8进3 + 26. 兵四进一 马4退6 + 27. 马三退四 车8进2 + 28. 马四进三 车8平5 + 29. 兵四进一 车5平2 + 30. 帅五平六 车2进3 + 31. 车七平九 马7退8 + 32. 兵四平五 象3进5 + 33. 马三进五 马8进6 + 34. 马五退三 马6进5 + 35. 马三进四 车2退4 + 36. 炮五退三 车2退1 + 37. 马四退五 车2平5 + 38. 帅六进一 炮3退7 + 39. 兵一进一 将5平6 + 40. 车九退二 车5进4 + 41. 车九进四 车5退4 + 42. 帅六退一 车5平3 + 43. 马八进九 炮3平4 + 44. 车九平二 车3进6 + 45. 帅六进一 车3退5 + 46. 帅六退一 卒1进1 + 47. 车二进五 将6进1 + 48. 车二退一 将6退1 + 49. 车二退五 车3进5 + 50. 帅六进一 车3退4 + 51. 帅六退一 炮4平3 + 52. 车二进六 将6进1 + 53. 车二退六 车3平4 + 54. 帅六平五 炮3平5 + 55. 仕五进四 车4平9 + 56. 车二进四 炮5平6 + 57. 马九进七 车9平3 + 58. 车二进一 将6退1 + 59. 车二进一 将6进1 + 60. 车二退六 炮6平3 + 61. 马七退八 车3进4 + 62. 马八退六 炮3平2 + 63. 车二平九 炮2进7 + 64. 车九退三 卒1进1 + 65. 仕四进五 卒1平2 + 66. 车九进二 卒2进1 + 67. 车九平六 将6退1 + 68. 车六进二 卒9进1 + 69. 仕五退四 炮2平1 + 70. 车六退二 卒2平3 + 71. 仕四退五 车3平2 + 72. 车六平九 卒9进1 + 73. 车九平四 将6平5 + 74. 车四平九 卒3平4 + 75. 车九进四 卒9平8 + 76. 车九退四 卒8平7 + 77. 车九进二 卒7平6 + 78. 车九平七 车2退4 + 79. 车七退四 炮1平2 + 80. 仕五进四 卒4进1 + 81. 仕四进五 士5退6 + 82. 帅五平四 卒4进1 + 83. 帅四进一 卒6进1 + 84. 车七进三 车2平7 + 85. 车七平五 卒6平5 + 86. 马六进八 车7进3 + 87. 帅四退一 卒4进1 + 0-1 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/14.\345\260\206\347\245\236\344\274\240\350\257\264(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(F3-D2).PGN" "b/CCGC/14.\345\260\206\347\245\236\344\274\240\350\257\264(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(F3-D2).PGN" new file mode 100644 index 0000000..2fcb93b --- /dev/null +++ "b/CCGC/14.\345\260\206\347\245\236\344\274\240\350\257\264(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(F3-D2).PGN" @@ -0,0 +1,42 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "14"] +[Date "2006.08.06"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "将神传说"] +[BlackTeam ""] +[Black "相眼竞技"] +[Result "1-0"] +[ECCO "B41"] +[Opening "五六炮左正马对反宫马"] +[Variation ""] + 1. 炮八平五 马8进7 + 2. 马八进七 炮2平4 + 3. 车九平八 马2进3 + 4. 兵七进一 卒7进1 + 5. 炮二平四 车9平8 + 6. 马二进三 炮8平9 + 7. 马七进六 车1进1 + 8. 车八进五 车1平6 + 9. 仕四进五 车8进5 + 10. 马六进五 马7进5 + 11. 炮五进四 炮4进5 + 12. 炮五退二 炮4平7 + 13. 炮四平五 车6进1 + 14. 兵三进一 车8退2 + 15. 兵三进一 车8进3 + 16. 兵三平四 车6平8 + 17. 兵四进一 炮7退2 + 18. 相三进一 卒3进1 + 19. 车八平七 炮7退4 + 20. 车七平三 炮9进4 + 21. 车一平四 炮7平3 + 22. 车三进四 将5进1 + 23. 兵四平五 将5平4 + 24. 车四进九 前车进3 + 25. 仕五退四 1-0 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/15.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\264\237)\346\242\246\345\205\245\347\245\236\346\234\272(D2-E2).PGN" "b/CCGC/15.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\264\237)\346\242\246\345\205\245\347\245\236\346\234\272(D2-E2).PGN" new file mode 100644 index 0000000..3e1b1ad --- /dev/null +++ "b/CCGC/15.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\264\237)\346\242\246\345\205\245\347\245\236\346\234\272(D2-E2).PGN" @@ -0,0 +1,48 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "15"] +[Date "2006.08.06"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "相眼竞技"] +[BlackTeam ""] +[Black "梦入神机"] +[Result "0-1"] +[ECCO "A42"] +[Opening "起马转边炮对进7卒"] +[Variation ""] + 1. 马八进七 卒3进1 + 2. 炮八平九 马2进3 + 3. 车九平八 车1平2 + 4. 车八进六 炮2平1 + 5. 车八平七 马8进7 + 6. 兵三进一 炮1退1 + 7. 马二进三 车9进1 + 8. 炮二平一 车9平4 + 9. 车七退一 炮8平9 + 10. 车一平二 炮1平3 + 11. 车七平四 马3进2 + 12. 兵七进一 车2进2 + 13. 兵七进一 马2进1 + 14. 马七进九 炮3进8 + 15. 帅五进一 卒7进1 + 16. 车四平六 车4进3 + 17. 兵七平六 车2进6 + 18. 帅五进一 车2退1 + 19. 帅五退一 车2平7 + 20. 炮九进四 车7进1 + 21. 帅五进一 马7进6 + 22. 车二进六 马6进4 + 23. 帅五平六 马4进2 + 24. 帅六平五 车7进1 + 25. 兵三进一 马2进3 + 26. 帅五平六 炮9平4 + 27. 炮九平六 车7退5 + 28. 帅六退一 车7平4 + 29. 帅六平五 车4进4 + 30. 帅五进一 车4平9 + 31. 炮六退五 0-1 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/16.\346\242\246\345\205\245\347\245\236\346\234\272(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(E2-D2).PGN" "b/CCGC/16.\346\242\246\345\205\245\347\245\236\346\234\272(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(E2-D2).PGN" new file mode 100644 index 0000000..82c0236 --- /dev/null +++ "b/CCGC/16.\346\242\246\345\205\245\347\245\236\346\234\272(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(E2-D2).PGN" @@ -0,0 +1,95 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "16"] +[Date "2006.08.06"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "梦入神机"] +[BlackTeam ""] +[Black "相眼竞技"] +[Result "1-0"] +[ECCO "B13"] +[Opening "中炮巡河炮对单提马横车"] +[Variation ""] + 1. 炮二平五 马2进3 + 2. 马二进三 车9进1 + 3. 车一平二 车9平4 + 4. 炮八进二 卒3进1 + 5. 炮八平五 士4进5 + 6. 马八进七 车4进6 + 7. 前炮平三 象7进9 + 8. 车九进二 卒7进1 + 9. 炮三平四 车1平2 + 10. 炮四进三 士5进6 + 11. 车二进七 马8进6 + 12. 车二平一 马6退4 + 13. 仕四进五 车4退1 + 14. 马三退四 车2进1 + 15. 炮五平四 炮2平1 + 16. 车一退一 车2进4 + 17. 相三进五 车2平6 + 18. 车一退二 马3进4 + 19. 车一平四 前马进6 + 20. 炮四进一 车4退3 + 21. 车九平八 炮1平3 + 22. 车八进三 象3进5 + 23. 车八平七 车4进6 + 24. 仕五退六 象5进3 + 25. 兵七进一 象3退1 + 26. 马七进六 炮3平5 + 27. 马四进三 马4进2 + 28. 兵一进一 卒5进1 + 29. 兵一进一 士6退5 + 30. 马六进四 炮5进1 + 31. 马四进五 马2进4 + 32. 马五进三 将5平4 + 33. 前马退二 炮5平7 + 34. 马三退五 炮7进3 + 35. 马五进七 马4退2 + 36. 炮四退二 马2进3 + 37. 炮四平六 将4平5 + 38. 炮六平二 马6进8 + 39. 兵一平二 马8进7 + 40. 帅五平四 马7退8 + 41. 兵二平三 马8退7 + 42. 炮二平一 将5平4 + 43. 兵七进一 象1进3 + 44. 马七进八 马3退5 + 45. 马八进九 象3退1 + 46. 马九进七 将4进1 + 47. 炮一平六 马5进3 + 48. 马二退四 马7进9 + 49. 兵九进一 炮7平6 + 50. 仕六进五 马9进8 + 51. 兵九进一 马8退7 + 52. 帅四平五 马7进8 + 53. 马四进二 炮6退3 + 54. 兵九进一 象1退3 + 55. 马二进三 炮6进5 + 56. 马七退五 将4退1 + 57. 炮六进一 马8进9 + 58. 兵九平八 马3进2 + 59. 帅五平四 炮6退6 + 60. 马五退七 炮6退1 + 61. 马七进六 马2退4 + 62. 马六进四 马4退5 + 63. 马三退四 马9退7 + 64. 后马进六 马5进4 + 65. 马六进七 马4退2 + 66. 马七退六 马2进4 + 67. 马六退五 马4进5 + 68. 马四退三 马5退6 + 69. 马五进六 马6退4 + 70. 马三进五 将4进1 + 71. 马六进八 士5进4 + 72. 炮六平九 将4平5 + 73. 马五进三 马4退2 + 74. 炮九进六 马2退4 + 75. 马三退四 将5退1 + 76. 马八退六 马7退6 + 77. 马四进三 将5平4 + 1-0 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/19.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\264\237)\346\243\213\345\244\251\345\244\247\345\234\243(D2-E1).PGN" "b/CCGC/19.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\264\237)\346\243\213\345\244\251\345\244\247\345\234\243(D2-E1).PGN" new file mode 100644 index 0000000..49ad662 --- /dev/null +++ "b/CCGC/19.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\264\237)\346\243\213\345\244\251\345\244\247\345\234\243(D2-E1).PGN" @@ -0,0 +1,58 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "19"] +[Date "2006.08.07"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "相眼竞技"] +[BlackTeam ""] +[Black "棋天大圣"] +[Result "0-1"] +[ECCO "C46"] +[Opening "中炮过河车互进七兵对屏风马平炮兑车"] +[Variation "红左边炮对黑退边炮上右士右直车"] + 1. 炮二平五 马8进7 + 2. 马二进三 车9平8 + 3. 车一平二 马2进3 + 4. 兵七进一 卒7进1 + 5. 车二进六 炮8平9 + 6. 车二平三 炮9退1 + 7. 马八进七 士4进5 + 8. 炮八平九 车1平2 + 9. 车九平八 炮9平7 + 10. 车三平四 马7进8 + 11. 车四平三 马8退7 + 12. 车三平四 炮2进4 + 13. 兵五进一 象7进5 + 14. 马三进五 车2进4 + 15. 车四进二 炮7退1 + 16. 车四退七 卒7进1 + 17. 马七进六 卒7进1 + 18. 兵七进一 车2平3 + 19. 车八进三 马7进6 + 20. 兵五进一 炮7进9 + 21. 仕四进五 马6进5 + 22. 车八平五 车3进5 + 23. 炮九平七 车8进9 + 24. 炮七进五 象5进3 + 25. 车四平二 车8退1 + 26. 炮七平三 炮7退7 + 27. 车五平三 炮7平4 + 28. 炮五进四 象3退5 + 29. 马六退八 车3退2 + 30. 车三平四 炮4进6 + 31. 马八退九 车3退3 + 32. 兵五平六 车3平4 + 33. 马九退七 炮4平2 + 34. 炮五退三 车4进2 + 35. 仕五进六 炮2进1 + 36. 帅五平四 将5平4 + 37. 帅四平五 车4进1 + 38. 车四进六 士5退6 + 39. 帅五平四 车4进2 + 40. 炮五退三 车4平5 + 0-1 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/20.\346\243\213\345\244\251\345\244\247\345\234\243(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(E1-D2).PGN" "b/CCGC/20.\346\243\213\345\244\251\345\244\247\345\234\243(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(E1-D2).PGN" new file mode 100644 index 0000000..35d1147 --- /dev/null +++ "b/CCGC/20.\346\243\213\345\244\251\345\244\247\345\234\243(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(E1-D2).PGN" @@ -0,0 +1,51 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "20"] +[Date "2006.08.07"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "棋天大圣"] +[BlackTeam ""] +[Black "相眼竞技"] +[Result "1-0"] +[ECCO "E02"] +[Opening "仙人指路进右马对飞象"] +[Variation ""] + 1. 兵七进一 象3进5 + 2. 马八进七 卒7进1 + 3. 炮二平六 车9进1 + 4. 马二进三 马2进4 + 5. 车一平二 卒3进1 + 6. 兵七进一 车1平3 + 7. 马七进八 炮2进5 + 8. 车二进七 炮2平7 + 9. 马八进九 马8进9 + 10. 车二退五 象5进3 + 11. 车二平三 车3进3 + 12. 马九退八 车3平2 + 13. 马八进六 车9平6 + 14. 炮六退一 车6进7 + 15. 仕四进五 车6退4 + 16. 车三平六 士6进5 + 17. 兵九进一 车6进2 + 18. 车六平四 车6进1 + 19. 仕五进四 马4进3 + 20. 炮六平七 马3进5 + 21. 兵五进一 马5退7 + 22. 车九进三 象7进5 + 23. 兵九进一 车2平4 + 24. 马六进四 卒5进1 + 25. 炮七平四 卒5进1 + 26. 马四退五 马7进5 + 27. 相七进五 车4进2 + 28. 车九平五 马9进7 + 29. 兵一进一 象3退1 + 30. 炮四平一 车4平1 + 31. 兵九平八 车1平2 + 32. 兵八平九 车2平1 + 33. 兵九平八 车1平2 + 1-0 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/21.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\264\237)\350\261\241\346\243\213\345\245\207\345\205\265(D2-F1).PGN" "b/CCGC/21.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\264\237)\350\261\241\346\243\213\345\245\207\345\205\265(D2-F1).PGN" new file mode 100644 index 0000000..09e463d --- /dev/null +++ "b/CCGC/21.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\350\264\237)\350\261\241\346\243\213\345\245\207\345\205\265(D2-F1).PGN" @@ -0,0 +1,72 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "21"] +[Date "2006.08.07"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "相眼竞技"] +[BlackTeam ""] +[Black "象棋奇兵"] +[Result "0-1"] +[ECCO "C23"] +[Opening "中炮过河车七路马对屏风马两头蛇"] +[Variation "红左横车兑三兵对黑高右炮"] + 1. 炮八平五 马2进3 + 2. 马八进七 车1平2 + 3. 车九平八 卒3进1 + 4. 车八进六 马8进7 + 5. 马二进三 卒7进1 + 6. 车一进一 炮8进1 + 7. 车八退二 象7进5 + 8. 兵七进一 卒3进1 + 9. 车八平七 马3进4 + 10. 车一平六 炮8进1 + 11. 车六平八 车2进1 + 12. 兵三进一 卒7进1 + 13. 车七平三 车9进1 + 14. 车八平六 车9平4 + 15. 车六进三 炮2平3 + 16. 马七进八 车2进4 + 17. 车六平八 马4进2 + 18. 车三平八 炮8平3 + 19. 仕六进五 后炮进7 + 20. 马三进四 后炮平6 + 21. 炮五平七 炮3平1 + 22. 炮二平三 士6进5 + 23. 兵一进一 卒5进1 + 24. 兵九进一 马7进5 + 25. 相三进五 马5进7 + 26. 炮三进二 车4进7 + 27. 炮三退三 车4退5 + 28. 炮三进三 卒9进1 + 29. 兵一进一 炮6平9 + 30. 车八退一 炮9进1 + 31. 车八平九 炮9平6 + 32. 车九退三 车4进3 + 33. 车九平六 车4平5 + 34. 车六进二 卒5进1 + 35. 相五退三 炮6进1 + 36. 车六进四 炮6平9 + 37. 炮三退二 车5平1 + 38. 车六平七 马7进6 + 39. 炮七平四 马6退4 + 40. 车七平九 炮9平5 + 41. 帅五平六 炮5平4 + 42. 炮四进一 车1进3 + 43. 帅六进一 炮4平1 + 44. 车九平八 车1退1 + 45. 帅六进一 卒5进1 + 46. 炮四退一 卒5平4 + 47. 帅六平五 卒4平3 + 48. 帅五平六 车1平3 + 49. 车八退三 马4进2 + 50. 帅六平五 卒3进1 + 51. 炮四平七 将5平6 + 52. 炮七平六 车3退5 + 53. 炮六进四 车3平4 + 54. 兵九进一 马2进3 + 0-1 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/22.\350\261\241\346\243\213\345\245\207\345\205\265(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(F1-D2).PGN" "b/CCGC/22.\350\261\241\346\243\213\345\245\207\345\205\265(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(F1-D2).PGN" new file mode 100644 index 0000000..94bfb8d --- /dev/null +++ "b/CCGC/22.\350\261\241\346\243\213\345\245\207\345\205\265(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(F1-D2).PGN" @@ -0,0 +1,69 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "22"] +[Date "2006.08.07"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "象棋奇兵"] +[BlackTeam ""] +[Black "相眼竞技"] +[Result "1-0"] +[ECCO "C42"] +[Opening "中炮过河车互进七兵对屏风马平炮兑车"] +[Variation "红七路马对黑退边炮"] + 1. 炮二平五 马8进7 + 2. 马二进三 车9平8 + 3. 车一平二 马2进3 + 4. 兵七进一 卒7进1 + 5. 车二进六 炮8平9 + 6. 车二平三 炮9退1 + 7. 马八进七 炮9平7 + 8. 车三平四 士4进5 + 9. 炮八平九 马7进8 + 10. 车九平八 车1平2 + 11. 炮九进四 卒7进1 + 12. 炮五进四 象3进5 + 13. 车四平三 马8退9 + 14. 车三退二 炮2进6 + 15. 炮五平六 卒3进1 + 16. 炮九退二 炮2退1 + 17. 兵七进一 炮2平7 + 18. 车八进九 马3退2 + 19. 炮九进五 马2进4 + 20. 车三平八 士5进6 + 21. 马七进六 车8进4 + 22. 车八进五 将5进1 + 23. 车八退一 车8平3 + 24. 炮六平五 象5进7 + 25. 炮五退二 车3平4 + 26. 马六进八 前炮进1 + 27. 炮九退三 后炮进1 + 28. 炮九进二 后炮进4 + 29. 马八进七 车4退2 + 30. 车八退三 马4进2 + 31. 车八进二 后炮进3 + 32. 帅五进一 前炮平8 + 33. 炮九退三 车4平3 + 34. 车八平七 将5平4 + 35. 车七退一 炮8退1 + 36. 帅五退一 炮7退3 + 37. 车七平六 将4平5 + 38. 炮九平五 将5平6 + 39. 车六平五 士6退5 + 40. 前炮平四 炮8进1 + 41. 帅五进一 炮7平6 + 42. 车五平四 士5进6 + 43. 车四平二 士6退5 + 44. 车二进二 将6进1 + 45. 车二退八 将6退1 + 46. 车二进八 将6进1 + 47. 车二退四 马9进7 + 48. 车二平四 象7退9 + 49. 车四退二 马7进6 + 50. 车四进二 将6平5 + 51. 帅五平六 象9进7 + 52. 炮四平五 1-0 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/23.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\345\222\214)\345\244\251\346\234\272(D2-F2).PGN" "b/CCGC/23.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\345\222\214)\345\244\251\346\234\272(D2-F2).PGN" new file mode 100644 index 0000000..f130871 --- /dev/null +++ "b/CCGC/23.\347\233\270\347\234\274\347\253\236\346\212\200(\345\205\210\345\222\214)\345\244\251\346\234\272(D2-F2).PGN" @@ -0,0 +1,128 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "23"] +[Date "2006.08.07"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "相眼竞技"] +[BlackTeam ""] +[Black "天机"] +[Result "1/2-1/2"] +[ECCO "D50"] +[Opening "中炮对列炮"] +[Variation ""] + 1. 炮二平五 炮2平5 + 2. 马二进三 马8进7 + 3. 马八进七 马2进3 + 4. 车一平二 车9平8 + 5. 车九平八 卒3进1 + 6. 车二进五 炮5平6 + 7. 炮八进二 炮8平9 + 8. 车二进四 马7退8 + 9. 炮八平九 象3进1 + 10. 兵三进一 士4进5 + 11. 马三进四 卒1进1 + 12. 炮九平六 炮6进2 + 13. 车八进四 炮9平6 + 14. 兵五进一 车1平4 + 15. 仕六进五 马8进9 + 16. 炮六退四 后炮平5 + 17. 兵七进一 车4进4 + 18. 兵五进一 车4平5 + 19. 马四退六 车5进1 + 20. 兵七进一 象1进3 + 21. 车八平五 炮5进3 + 22. 马六进七 马3进4 + 23. 前马进八 马9退7 + 24. 马七进六 马4退3 + 25. 炮六进二 炮6进1 + 26. 马八退七 炮6平4 + 27. 马七退五 炮4平7 + 28. 相三进一 炮7进3 + 29. 马五进六 马7进5 + 30. 炮五进五 象7进5 + 31. 马六进七 将5平4 + 32. 马七退五 马3进2 + 33. 马五进七 马2进1 + 34. 马七退六 将4平5 + 35. 炮六平二 士5进6 + 36. 炮二平五 将5平4 + 37. 马六退五 马1进3 + 38. 炮五进四 马3退4 + 39. 炮五平一 炮7平9 + 40. 炮一退二 马4进2 + 41. 仕五退六 炮9退2 + 42. 仕四进五 炮9平8 + 43. 帅五平四 炮8退5 + 44. 炮一平四 士6进5 + 45. 相一进三 炮8平7 + 46. 相三退五 将4平5 + 47. 仕五进六 炮7平8 + 48. 仕六进五 炮8平9 + 49. 炮四平三 马2进3 + 50. 仕五进四 马3退4 + 51. 仕四退五 马4退6 + 52. 马五进七 卒7进1 + 53. 炮三平一 炮9进2 + 54. 帅四平五 炮9平2 + 55. 马七退八 炮2进1 + 56. 相五退三 马6进8 + 57. 炮一平五 将5平6 + 58. 马八进九 卒7进1 + 59. 炮五进一 马8进7 + 60. 帅五平四 马7退6 + 61. 炮五平七 士5进4 + 62. 马九进八 卒7进1 + 63. 相七进五 炮2退1 + 64. 马八退六 卒7进1 + 65. 炮七平四 士6退5 + 66. 炮四平八 炮2平3 + 67. 炮八退一 炮3进1 + 68. 炮八平四 将6平5 + 69. 马六退四 卒7进1 + 70. 马四进二 炮3平8 + 71. 相三进一 炮8平5 + 72. 炮四进二 炮5退1 + 73. 马二退四 马6进8 + 74. 马四退三 将5平4 + 75. 仕五进四 马8退6 + 76. 仕六退五 士5进6 + 77. 马三进四 马6退5 + 78. 相一进三 卒7平8 + 79. 炮四平一 马5进4 + 80. 炮一平三 马4进3 + 81. 炮三平四 将4平5 + 82. 马四退六 炮5进1 + 83. 炮四平六 炮5平4 + 84. 炮六平五 炮4平7 + 85. 马六进四 炮7退3 + 86. 马四进五 士4退5 + 87. 炮五平六 马3退4 + 88. 马五退六 马4进2 + 89. 炮六平五 将5平6 + 90. 马六退八 卒8平7 + 91. 炮五平八 马2退1 + 92. 炮八平三 士5进4 + 93. 帅四平五 马1进2 + 94. 炮三平八 马2进1 + 95. 马八退六 炮7平6 + 96. 相五退七 炮6平4 + 97. 马六进七 士6退5 + 98. 帅五平四 马1退2 + 99. 炮八平三 卒7平8 +100. 相七进五 马2退4 +101. 马七退六 炮4进5 +102. 帅四平五 炮4退3 +103. 仕五进六 将6平5 +104. 仕四退五 炮4平2 +105. 炮三退一 炮2进1 +106. 仕五进四 士5进6 +107. 帅五平六 炮2平5 +108. 仕四退五 炮5平4 +109. 帅六平五 炮4平3 +110. 帅五平四 炮3退3 +111. 炮三进三 1/2-1/2 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git "a/CCGC/24.\345\244\251\346\234\272(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(F2-D2).PGN" "b/CCGC/24.\345\244\251\346\234\272(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(F2-D2).PGN" new file mode 100644 index 0000000..e016e6b --- /dev/null +++ "b/CCGC/24.\345\244\251\346\234\272(\345\205\210\350\203\234)\347\233\270\347\234\274\347\253\236\346\212\200(F2-D2).PGN" @@ -0,0 +1,58 @@ +[Game "Chinese Chess"] +[Event "1st Chinese Computer Games Championship, Beijing 2006"] +[Round "24"] +[Date "2006.08.07"] +[Site "北京中国科技馆"] +[RedTeam ""] +[Red "天机"] +[BlackTeam ""] +[Black "相眼竞技"] +[Result "1-0"] +[ECCO "E11"] +[Opening "仙人指路飞相对卒底炮"] +[Variation ""] + 1. 兵七进一 炮2平3 + 2. 相三进五 马2进1 + 3. 马八进七 车1平2 + 4. 马七进六 马8进7 + 5. 车九进一 车9进1 + 6. 车九平四 卒7进1 + 7. 车四进三 炮3平5 + 8. 仕四进五 炮5进4 + 9. 马二进四 炮5退2 + 10. 兵三进一 卒3进1 + 11. 兵三进一 卒3进1 + 12. 马六退四 车2进6 + 13. 兵三进一 马7退5 + 14. 炮二平四 炮8退2 + 15. 车一平二 车9退1 + 16. 炮八平七 马5进4 + 17. 车四进二 炮5进1 + 18. 炮七进七 士4进5 + 19. 前马进三 车2退2 + 20. 马三退五 马4进5 + 21. 车四平五 马5进6 + 22. 仕五进四 炮8进4 + 23. 炮七平四 将5平6 + 24. 车五进二 车9进2 + 25. 仕四退五 车9平6 + 26. 马四进三 车6退1 + 27. 车五平四 将6进1 + 28. 马三进二 车2平6 + 29. 马二进三 将6进1 + 30. 车二进六 将6平5 + 31. 兵三平四 车6进2 + 32. 兵四平五 将5平6 + 33. 车二进二 马1进3 + 34. 马三进一 车6平8 + 35. 车二退五 马3进5 + 36. 马一进三 将6退1 + 37. 车二进五 将6退1 + 38. 车二进一 将6进1 + 39. 兵五进一 卒3进1 + 40. 兵五进一 将6退1 + 41. 马三退四 1-0 +============================ +欢迎访问《象棋百科全书》网站 +推荐用“象棋巫师”观赏棋谱 +http://www.elephantbase.net/ diff --git a/DOC/eleeye_book.htm b/DOC/eleeye_book.htm new file mode 100644 index 0000000..e8c0ec3 --- /dev/null +++ b/DOC/eleeye_book.htm @@ -0,0 +1,169 @@ + + + + + +中国象棋程序设计探索(七):开局库 + + + + +
+
+
中国象棋程序设计探索
+
+
 
+
+
黄晨 * 20056月初稿,20075月修订
+
+
( * 上海计算机博弈研究所,eMailwebmaster@elephantbase.net)
+
+
 
+
() 开局库
+
 
+
  在阅读本章前,建议读者先阅读象棋百科全书网中《对弈程序基本技术》专题的以下几篇译文:
+
  (1) 其他策略——开局库(Martin Fierz)
+
 
+
7.1 象棋程序对开局库的处理
+
 
+
  开局库是象棋程序中知识含量最多的部分,各种程序的棋力会因为开局库的不同而有所差距。互联网上介绍国际象棋开局库的文章很多,而中国象棋开局库的原理和国际象棋是完全一样的,因此笔者就不作太多的介绍了。
+
  很多国际象棋的程序中,开局库并不被引擎处理,而是由界面来完成的,棋局进入中局脱离棋谱后,才让引擎作搜索。ChessBase的系列软件JuniorFritz,以及支持UCI协议的Shredder,都使用这种工作方式,由于它们使用同一套ChessBase的界面,因此开局库格式是统一的。由于WinBoard本身并不能处理开局库,因此支持WinBoard的引擎都具有处理开局库的能力,而且每个引擎都有各自的开局库格式。
+
  而中国象棋目前没有统一的界面,因此也没有统一的开局库格式,开局库一般由引擎来处理,各种引擎有各自定义的开局库格式。
+
  ElephantEye早期开局库具有非常明显的特点——它是文本格式的,每一行记录一个着法,依次是着法(红色部分)、权重(绿色部分)和局面(紫色部分)
+
 
+
b2e2 5895 + rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR + w - - 0 1
+
 
+
  由于记录局面时使用FEN串,这就增加了开局库的可读性,但同时使开局库变得特别庞大,ElephantEye的开局库(BOOK.DAT)仅有20,000多个着法却达到了1.6M
+
  从ElephantEye 2.0以后,每个局面仅占用8字节(4字节的Zobrist键值、2字节的着法和2字节的权重),而且对称局面作了合并,因此由10,000多个着法组成的开局库大小不超过100K
+
 
+
7.2 开局库的制作
+
 
+
  ElephantEye现有的开局库是从象棋百科全书网上收录的8000多局对局中整理出来的,这些对局涵盖了1990年到2004年的全国顶级象棋比赛(团体赛、个人赛、甲级联赛和五羊杯),因此具有代表性。现在简要介绍一下开局库的制作流程:
+
  (1) 把所有的对局中的每个着法解析出来,记录到临时文件中,临时文件的每个着法记录占16字节(8字节的Zobrist键值、4字节的着法和2字节的权重)
+
  (2) 对临时文件的着法记录按Zobrist键值(第一关键字)和着法(第二关键字)排序;
+
  (3) Zobrist键值和着法作关键字,对权重进行叠加;
+
  (4) 按照笔者观点,权重 + = 胜局数 ×3 + 和棋局数 - 负局数,因此在第(1)步中,导致胜局的着法的权重置3,和局置1,负局置-1
+
  (5) 过滤掉权重小于4的着法(即收入开局库的着法至少是一胜一和的),把Zobrist键值的后4字节、2字节表示的着法和权重除以416位整数值记录到开局库文件(BOOK.DAT)中。
+
 
+
7.3 开局着法的选择
+
 
+
  象棋程序在处理一个局面时,首先要从开局库中寻找是否有相同的局面,当开局库中找不到局面时,才会调用搜索程序进行思考。ElephantEye从开局库中找到着法的过程如下:
+
  (1) 由于开局库是按Zobrist键值排序的,因此用二分查找法可以很快找到所有符合的局面及其着法;
+
  (2) 如果当前局面在开局库中没有,那么查找对称局面,找到的着法也要作对称处理;
+
  (3) 由于开局库中的Zobrist键值只有4字节,因此有可能产生冲突,必须从着法列表中排除不合理着法;
+
  (4) 用随机数根据着法的权重选择着法;
+
  (5) 如果该着法没有构成重复局面,那么将该着法作为最佳着法直接返回。
+
+ + +
  • 上一篇 中国象棋程序设计探索():并行搜索技术探索
  • +
  • 下一篇 中国象棋程序设计探索():后台思考和时间策略
  • +
  • 返 回 象棋百科全书——电脑象棋 +
  • +
    +
    + + + + + + + + +

    +
    www.elephantbase.net
    +
    + + diff --git a/DOC/eleeye_evaluate.htm b/DOC/eleeye_evaluate.htm new file mode 100644 index 0000000..7895e63 --- /dev/null +++ b/DOC/eleeye_evaluate.htm @@ -0,0 +1,79 @@ + + + + + +中国象棋程序设计探索(九):局面评价函数 + + + + +
    +
    +
    中国象棋程序设计探索
    +
    +
     
    +
    +
    黄晨 * 20056月初稿,20075月修订
    +
    +
    ( * 上海计算机博弈研究所,eMailwebmaster@elephantbase.net)
    +
    +
     
    +
    () 局面评价函数
    +
     
    +
      在阅读本章前,建议读者先阅读象棋百科全书网中《对弈程序基本技术》专题的以下几篇译文: +
    +
      (1) 局面评估函数——简介()(David Eppstein)
    +
      (2) 局面评估函数——简介()(David Eppstein)
    +
    + + +
  • 上一篇 中国象棋程序设计探索():后台思考和时间策略
  • +
  • 下一篇 中国象棋程序设计探索():实用程序片段
  • +
  • 返 回 象棋百科全书——电脑象棋 +
  • +
    +
    + + + + + + + + +

    +
    www.elephantbase.net
    +
    + + diff --git a/DOC/eleeye_heuristic.htm b/DOC/eleeye_heuristic.htm new file mode 100644 index 0000000..ff65587 --- /dev/null +++ b/DOC/eleeye_heuristic.htm @@ -0,0 +1,298 @@ + + + + + +中国象棋程序设计探索(四):启发算法 + + + + +
    +
    +
    中国象棋程序设计探索
    +
    +
     
    +
    +
    黄晨 * 20056月初稿,20075月修订
    +
    +
    ( * 上海计算机博弈研究所,eMailwebmaster@elephantbase.net)
    +
    +
     
    +
    () 启发算法
    +
     
    +
    4.1 启发算法概述
    +
     
    +
      Laramée的《国际象棋程序设计():基本搜索方法》连载中曾经提到,Alpha-Beta搜索的结点数,数量相当于最笨的“最小-最大”搜索的平方根。严格来说,如果搜索树在每个结点的分枝因子都是b,那么dAlpha-Beta搜索在最好情况下搜索的结点数是:b[d / 2] + + b[(d + 1) / 2] - 1(都是指叶子结点,其中b[d / 2] - 1个是Alpha结点,b[(d + 1) / + 2] - 1个是Beta结点,1个是PV结点),这样的搜索树称为“最小树”。因此,让Alpha-Beta的搜索树尽可能地接近最小树是非常重要的,相关的措施在广义上都称为“启发”(Heuristic)
    +
      由于Alpha-Beta搜索对着法顺序非常敏感,因此对着法进行排序,是体现Alpha-Beta算法效率的关键所在,这就是我们狭义上所说的启发,即通过排序尽量首先搜索最好的着法。着法启发通常有置换表启发、吃子启发、杀手着法启发和历史表启发这四种最为常用的算法。广义上的启发还可以通过改变窗口宽度来实现,因为改变窗口宽度也可以提高Alpha-Beta的截断效率,因此很多Alpha-Beta的变种算法实际上都属于启发这个范畴,例如期望窗口、PVSMTD(f)
    +
      本章我们只讨论着法启发。
    +
     
    +
    4.2 静态着法启发
    +
     
    +
      静态着法启发是指不依赖于搜索结果的着法排序方式,即程序分析了某个局面后,不经过搜索,就大致应该知道哪些着法应该首先尝试。在上面提到的4种着法启发中,吃子启发是静态着法启发,因为吃子着法数量不多,而且往往很多情况能立刻得到实惠,所以需要首先考虑。而置换表启发、杀手着法启发和历史表启发,都依赖于以前搜索的结果,因此属于动态着法启发的范畴。
    +
      在ElephantEye中,吃子着法是有选择的,即“表面上立刻能得到实惠”的着法。为此ElephantEye用了一个称为MVV(LVA)的策略,在被吃子有保护的情况下,考虑MVV(被吃子价值)-LVA(攻击子价值)的值,而在被吃子没保护的情况下,只考虑MVV的值,然后对这种策略产生的MVV(LVA)值作排序,并选择MVV(LVA)大于零的着法首先搜索。为此,<genmoves.cpp>提供了一个称为“Protected()”的函数,对某个棋子是否有保护作快速判断。
    +
      吃子启发仅仅是静态着法启发的最明显的表现,事实上这类启发还体现在其他地方。当搜索进行初期,置换表、杀手着法、历史表等动态着法启发还没起作用,此时着法生成的顺序就起了主要作用。因此,在着法生成时,考虑首先生成车的着法,最后生成帅()的着法,往往是很有效的;而在生成车的着法时,首先生成车向前走的着法,然后生成车向后走的着法,也能起到一定的启发作用。因此,这种静态着法启发也是很多程序所考虑的。
    +
     
    +
    4.3 + 置换表启发和低出(高出)边界的修正
    +
     
    +
      在置换表中保存一个着法,一方面可以利用置换表来获得主要变例,但最主要的作用是置换表启发。在探测置换表时,尽管局面命中但深度没达到要求时,至少可以得到一个着法,这个着法应该首先搜索,几乎所有的象棋程序都是这么做的。哪些着法应该被保存到置换表中呢?实践证明,PV结点中的最佳着法,以及Beta结点中产生截断的着法,都应该被保存到置换表中,而Alpha结点尽管也要保存,但不要保存着法(置换表着法是空的),因为Alpha结点的每个着法都返回Alpha值,我们不知道哪个着法是最好的。
    +
      显然,当探测置换表时找到PV结点或Beta结点(但深度不够),就会有需要首先搜索的置换表着法。那么,找到Alpha结点时,是否还会有置换表着法呢?中国象棋程序“纵马奔流”采取了一个称为“低出(高出)边界的修正”策略,使得某些Alpha结点也存在置换表着法。
    +
      “低出边界的修正”指的是,当某个Alpha结点覆盖某个相同局面的置换表项时,保留该表项的最佳着法。相应地,当某个Beta结点没能覆盖某个相同局面的置换表项(该表项记录了一个没有着法的Alpha结点)时,只把这个Beta结点的截断着法写入该置换表项,称为“高出边界的修正”。在前一章介绍的双层置换表的覆盖策略中,第一层(深度优先)需要考虑“低出边界的修正”和“高出边界的修正”,而第二层(始终覆盖)则只需要考虑“低出边界的修正”。
    +
     
    +
    4.4 杀手着法启发
    +
     
    +
      杀手着法启发(Killer + Heuristic)是基于这样一个思想,搜索某个结点时首先尝试着法a1,由a1的后续着法b1产生截断,回到原来的结点时再搜索a1的兄弟结点a2时,如果b1仍旧是a2的后续着法,那么b1很有可能也会产生截断。
    +
      采用杀手着法启发时,需要构造一个称为KillerMoves[MaxPly]的全局数组。搜索到深度为Ply的结点时,首先尝试KillerMoves[Ply]的着法(前提是该着法在当前局面下是合理的),搜索完这个结点时,把产生截断的着法(如果有的话)记录到KillerMoves[Ply]。由此产生的效果,就是当亲兄弟结点没有杀手着法时,会找到堂兄弟结点的杀手着法。
    +
      大多数程序会为每层分配2个杀手着法,并采用先进先出的方式管理,即:
    +
     
    +
    if (CutMove != KillerMoves[Ply][0]) {
    +
     KillerMoves[Ply][1] = KillerMoves[Ply][0];
    +
     KillerMoves[Ply][0] = CutMove;
    +
    }
    +
     
    +
      而搜索结点时,先搜索最近保存的杀手着法(KillerMoves[Ply][0]),再搜索比较旧的那个(KillerMoves[Ply][1])
    +
     
    +
    4.5 历史表启发
    +
     
    +
      “历史表启发”(History + Heuristic)是杀手着法启发的扩展,历史表记录的是整个搜索树中着法的好坏。历史表的思想是:搜索树中某个结点上的一个好的着法,对于其他结点可能也是好的。没有什么非常可靠的理由来支持这个思想,但根据历史表来排序着法,总比不排序要好得多,而且实践证明这是一种效果非常好的启发算法,几乎每个程序都用到。
    +
      历史表的处理方法,在各个程序中都有所差异,主要反映在以下几个方面:
    +
      (1) 产生全部着法时,是否完全根据历史表来排序。很多程序先产生吃子的着法,用MVV/LVA来排序,然后产生不吃子的着法,根据历史表来排序。目前ElephantEye在生成不吃子的着法后就按照历史表来排序,被排序的着法包括吃子启发没有搜索到的吃子着法和不吃子的着法。
    +
      (2) 找到好的着法时,在历史表中增加多少值。ElephantEye采用通常的n2(n为当前结点需要搜索的深度)的策略,而也有的程序设计师偏爱传统的2n。如果不确定哪个更好,那么不妨试试斐波那契数列(12358……)
    +
      (3) 什么样的着法算是好的着法。ElephantEye认为产生Beta截断的着法和PV结点中最好的着法都是好的着法(杀手着法也是这样认定的),而且这两类着法在历史表中增加同样多的值。也有的程序则为PV结点的最好着法增加更多的值。
    +
      (4) 历史表应该用什么样的结构。ElephantEye只设立一个[256][256]的数组,红方和黑方的着法都记录在这个数组中,更多的程序则是红方着法和黑方着法分别记录的,例如国际象棋程序Fruit用的历史表是[12][64]的,前一个指标代表兵种,后一个指标代表目标格。
    +
     
    +
      尽管历史表处理起来非常简单,笔者也不打算列出操作历史表的代码,但是历史表中出现的问题远不止以上这几点。
    +
      很重要的一点是:历史表受置换表和迭代加深的影响很大。根结点做浅一层搜索时,历史表中已经记录了丰富的信息,因此深一层的搜索就可以充分利用这些信息来做更好的启发。反过来,如果根结点不做迭代加深,直接开始深层次的搜索,那么历史表启发的效率就会大幅度下降,因此这就引发出清空历史表的问题。
    +
      思考完一个着法时是否清空置换表,各个程序有不同的做法。ElephantEye是彻底清空置换表的,因此下一次搜索时根结点总是迭代加深的。而是否清空历史表,则是更复杂的问题,它与是否清空置换表有关,ElephantEye在清空置换表的同时也清空历史表。
    +
      如果每次搜索时不清空置换表,那么根结点浅层分值在置换表中已经找到,程序就直接做深层次的搜索,如果历史表是空的,那么历史表的启发效率就非常低了,因此不清空置换表而去清空历史表是不明智的做法,这样会导致“历史表信息丢失”。那么就不对历史表作处理吗?历史表中的数据会日积月累,而大部分数据是早期搜索时留下的,有可能开局时的历史着法信息被保留到了残局,这样当然更会影响历史表启发,导致“历史表信息污染”。
    +
      笔者提出一个“历史表衰减”的方法,程序每次思考一个新的局面时,如果不清空置换表,那么就对历史表中的每项数据做一个衰减,可以尝试衰减为原来的一半或1/4,在“历史表信息丢失”和“历史表信息污染”之间作一个权衡。
    +
     
    +
    4.6 总体着法顺序
    +
     
    +
      以上我们主要介绍了四种启发算法,即吃子启发、置换表启发、杀手着法启发和历史表启发。现在我们要考虑如何把这四种算法结合起来。ElephantEye和大多数程序一样,采用了以下的顺序:
    +
      (1) 置换表启发;
    +
      (2) 吃子启发(之前生成所有吃子着法)
    +
      (3) 杀手着法启发;
    +
      (4) 历史表启发(之前生成所有不吃子着法)
    +
      这个顺序用一个MoveSortStruct的结构来维护(参阅<movesort.cpp>),使得搜索例程变得格外简单:
    +
     
    +
    Move = MoveSort.Next();
    +
    while (Move != NullMove) {
    +
     MakeMove(Move);
    +
     ……
    +
     Move = MoveSort.Next();
    +
    }
    +
     
    +
      我们感兴趣的是Next()这个例程,它要求按照以上4个不同的启发阶段来给出着法,但当某个阶段没有着法时,不跳出例程而直接进入下一个阶段(最后一个阶段则直接给出NullMove)ElephantEyeCraftyFruit等程序一样,用了不带breakswitch...case这样一个炫技。MoveSortStruct中有一个称为Phase(阶段)的变量,记录了当前的启发阶段,除了以上4个阶段外,还包括两个:
    +
      (1) 在吃子启发前,是生成吃子着法的阶段;
    +
      (2) 在历史表启发前,是生成不吃子着法的阶段。
    +
     
    +
    MoveStruct MoveSortStruct::Next(void) {
    +
     switch (Phase) {
    +
     case HASH_MOVE:
    +
      Phase = GEN_CAP_MOVES;
    +
      if (HashMove != NullMove) {
    +
       return HashMove;
    +
      }
    +
      // 直接进入下一个"case",下同。
    +
     case GEN_CAP_MOVES:
    +
      Phase = CAP_MOVES;
    +
      MoveNum = GenCapMoves(MoveList);
    +
     case CAP_MOVES:
    +
      i ++;
    +
      if (i < MoveNum) {
    +
       return MoveList[i];
    +
      }
    +
     case KILLER_MOVE:
    +
      Phase = GEN_NONCAP_MOVES;
    +
      if (KillerMove != NullMove && LegalMove(KillerMove)) + {
    +
       return KillerMove;
    +
      }
    +
      ……
    +
     }
    +
    }
    +
    + + +
  • 上一篇 中国象棋程序设计探索():搜索和置换表
  • +
  • 下一篇 中国象棋程序设计探索():克服水平线效应
  • +
  • 返 回 象棋百科全书——电脑象棋
  • +
    +
    + + + + + + + + +

    +
    www.elephantbase.net
    +
    + + diff --git a/DOC/eleeye_horizon.htm b/DOC/eleeye_horizon.htm new file mode 100644 index 0000000..fbb46aa --- /dev/null +++ b/DOC/eleeye_horizon.htm @@ -0,0 +1,292 @@ + + + + + +中国象棋程序设计探索(五):克服水平线效应 + + + + +
    +
    +
    中国象棋程序设计探索
    +
    +
     
    +
    +
    黄晨 * 20056月初稿,200712月修订
    +
    +
    ( * 上海计算机博弈研究所,eMailwebmaster@elephantbase.net)
    +
    +
     
    +
    () 克服水平线效应
    +
     
    +
      在阅读本章前,建议读者先阅读象棋百科全书网中《对弈程序基本技术》专题的以下几篇译文:
    +
      (1) 高级搜索方法——简介()(David Eppstein)
    +
      (2) 高级搜索方法——静态搜索(Bruce Moreland)
    +
      (3) 高级搜索方法——空着裁剪(Bruce Moreland)
    +
      (4) 其他策略——重复检测(Bruce Moreland)
    +
      (5) 其他策略——藐视因子(Bruce Moreland)
    +
     
    +
      在象棋程序的搜索技术中,裁剪和延伸是最有挖掘潜力之处,在各个程序中的差异也比较大。ElephantEye在这方面没有花太多的笔墨,空着裁剪、静态搜索和选择性延伸跟其他程序大同小异,读者可能会认为“历史表裁剪”(History Pruning)ElephantEye的亮点,但是笔者对该算法的原理并未完全把握,而且很多细节还有待摸索,所以这里就不作介绍,有兴趣的读者可参考ElephantEye源程序中<search.cpp>的相关注释。笔者将对几个比较成熟可靠的算法作一些介绍。
    +
     
    +
    5.1 无害裁剪
    +
     
    +
      很多局面不需要搜索即可知道它的确切评价值,或者至少超出Alpha-Beta窗口,此时就可以把该结点以下的搜索树裁剪掉,而不影响搜索结果,这类裁剪称为“无害裁剪”,ElephantEye使用了以下几种无害裁剪:
    +
      (1) 杀棋步数(DTM)裁剪。由于DTM调整的缘故,在深度为Ply的结点中,搜索结果不会落在窗口(Ply - MATE_VALUE, MATE_VALUE - Ply)外,所以当(Alpha, Beta)窗口和该窗口没有交叠时,立即可以作出裁剪。裁剪有两种情况,要么MATE_VALUE - Ply + <= Alpha,要么Ply + - MATE_VALUE >= BetaElephantEye只考虑了后者。
    +
      (2) 重复裁剪。如果出现重复局面,那么应当根据规则直接判和或判某方长打作负,所以应当返回相应的分值。尽管象棋规则规定局面重复三次或更多次才予以判定,但在搜索过程中只要遇到一次重复,继续搜索下去就会有第二次、第三次,所以ElephantEye只要遇到一次重复就不再搜索下去,但根结点要另外处理(否则一个着法也没搜索,就出不了子了)
    +
      (3) 和棋裁剪。如果双方都没有明显可以杀死对方的子力,即可返回和棋分值(0或藐视因子),而不必继续往下搜索。ElephantEye把双方都只剩下仕()()的局面规定为和棋局面。
    +
      (4) 置换裁剪。置换表的一个作用就是利用置换现象裁剪掉某些分枝,前面已经作了详细的介绍。这里要提的是,由于存在“搜索的不稳定性”的原因,置换裁剪并非绝对无害的,ElephantEye就不记录“利用长将判负策略搜索到的局面”,前面也有所介绍。
    +
     
    +
    5.2 带检验的空着裁剪
    +
     
    +
      “带检验的空着裁剪”(Verified Null-Move Pruning)指的是检验空着裁剪是否安全的算法,它首先由Tabibi发表在2002年的ICGA(ICCA)杂志上(参阅Tabibi OD, Netanyahu NS: Verified + Null-Move Pruning, ICGA J. 25 (3): + 153-161, 2002,可以从互联网上找到)。其内容可以概括为: +
    +
      (1) R = 3的空着裁剪进行搜索;
    +
      (2) 如果高出边界,那么做浅一层的搜索(这就意味着一层的搜索是无法做带验证的空着裁剪的)
    +
      (3) 做浅一层的搜索时,子结点用R = 3的不带验证的空着裁剪;
    +
      (4) 如果浅一层的搜索高出边界,那么带验证的空着裁剪是成功的,否则必须重新做完全的搜索。
    +
      笔者认为这里存在很多问题:
    +
      (1) R = 3非常冒进,还是用R = 2比较合适;
    +
      (2) 检验时做浅一层的搜索太浪费时间,裁剪的层数可以跟空着裁剪一样的R值一样,而且窗口也用以Beta为界的零窗口;
    +
      (3) 做检验时,子结点仍旧应该做带检验的空着裁剪,否则“连停着杀”就检测不出来了。
    +
      ElephantEye是否启用空着裁剪,分三种情况讨论:
    +
      (1) 我方进攻子力达到3个,就使用不带检验的空着裁剪;
    +
      (2) 我方进攻子力小于3个,则使用带检验的空着裁剪;
    +
      (3) 我方只有仕()()等守子,则禁用空着裁剪。
    +
      因此,ElephantEye的代码中,和空着裁剪有关的部分如下:
    +
     
    +
    const int NULL_REDUCTION = 2
    +
     
    +
    int AlphaBeta(int Alpha, int Beta, int Depth, bool Verify + = false) {
    +
     ……
    +
     if (!Verify && !InCheck() && NullOkay()) + {
    +
      MakeNull();
    +
      Value = -AlphaBeta(-Beta, 1 - Beta, Depth - 1 - + NULL_REDUCTION);
    +
      UndoNull();
    +
      if (Value >= Beta + && (NullSafe() || AlphaBeta(Beta - 1, Beta, Depth + - NULL_REDUCTION, true) >= Beta)) {
    +
       return Value;
    +
      }
    +
     }
    +
     ……
    +
    }
    +
     
    +
      这个技术使得ElephantEye在残局中仍旧能启用空着裁剪,而且不会出现走错的情况,因此ElephantEye在几乎不带残局知识的情况下,残局的棋力还是非常高的。
    +
     
    +
    5.3 静态搜索
    +
     
    +
      静态搜索尽管实现起来比较简单,但是很多技术细节仍旧是需要考虑的,问题主要有以下几个:
    +
      (1) 如果结点被将军,是否要产生全部着法。绝大多数程序都没有考虑结点被将军的情况,因为将军判断是很花费时间的。而ElephantEye在将军判断上有优势,因此在将军时产生全部的着法。因此有些多步连杀的排局,如果每步将军都吃子,那么ElephantEye只需要搜索1层就可以解出了,因此静态搜索中判断将军对于搜索杀棋是很有好处的。
    +
      (2) 如何使用MVV/LVA启发。MVV/LVA是静态搜索最常用的启发方式,如果棋盘的数据结构精心设计,SEE也是值得尝试的。尽管MVV/LVA很简单,但是究竟根据“被吃子价值-攻击子价值”来排序,还是先排序“被吃子价值”,相同的情况再排序“攻击子价值”呢?ElephantEye是以MVV(LVA)的值为依据的,可参考前一章(吃子启发)
    +
      (3) 是否需要用其他搜索或启发算法。常规搜索中的很多搜索算法都不适合于静态搜索,这就是静态搜索简单的缘故。静态搜索本身就是空着启发的(第一个着法总是空着),但由于没有深度参数,所以不可能使用空着裁剪。另外,几乎所有的程序都不在静态搜索中使用PVS、置换表、杀手着法启发、历史表启发等算法。
    +
      (4) 是否所有的吃子都需要考虑。如果搜索全部吃子着法,那么程序在叶子结点上花费的时间就非常浪费。ElephantEye在搜索所有吃子着法时,对着法作了过滤——吃不过河的兵和吃仕()()的着法都不搜索了,尽管仕()和相()ElephantEye的子力评价中分数很高(轻子价值的20%40%),但静态搜索本身就是对局面的粗略评价。
    +
     
    +
    5.4 选择性延伸
    +
     
    +
      常用的选择性延伸有这几种策略:(1) 将军延伸;(2) 单一应着延伸;(3) 杀棋威胁延伸;(4) 兑子延伸;(5) 通路兵挺进延伸。ElephantEye只考虑了前两种,代码很简单:
    +
     
    +
    int AlphaBeta(int Alpha, int Beta, int Depth) {
    +
     ……
    +
     MoveStruct ThisMove = NextMove();
    +
     while (ThisMove != NullMove) {
    +
      MakeMove(ThisMove);
    +
      NewDepth = (InCheck() || + SingleReply() || MateThreat() + || ReCapture() ? Depth : + Depth - 1);
    +
      Value = Search(-Beta, -Alpha, NewDepth);
    +
      UndoMove();
    +
      ……
    +
      ThisMove = NextMove();
    +
     }
    +
     ……
    +
    }
    +
     
    +
      ElephantEye的早期版本采用了杀棋威胁延伸和兑子延伸(以上代码中的蓝色部分),尽管现在已经不再使用了,但这里不妨介绍一下。
    +
      杀棋威胁延伸指的是,对方走出一步催杀的棋时,需要多搜索一层,而判断对方是否催杀则是利用空着裁剪,为此空着裁剪的代码应该作适当的修改:
    +
     
    +
    ……
    +
    MateThreat = false;
    +
    if (!InCheck() && NullOkay()) {
    +
     MakeNull();
    +
     Value = -AlphaBeta(-Beta, 1 - Beta, Depth - 1 - NULL_REDUCTION);
    +
     UndoNull();
    +
     if (Value >= Beta) {
    +
      return Value;
    +
     } else if (Value == Ply + 2 - + MATE_VALUE) {
    +
      MateThreat = true;
    +
     }
    +
    }
    +
    ……
    +
     
    +
      如果本方走了空着,而立即被对方将死(该结点的深度是Ply + 2),那么返回值作DTM调整将变成Ply + 2 - MATE_VALUE,因此该值就成为判断是否催杀的依据。
    +
      兑子延伸指的是,遇到连续两个吃子着法,并且吃同一格子的子,这样的着法称为“吃回”(ReCapture),需要多搜索一层,为此判断两个着法是否吃回的函数可以写成:
    +
     
    +
    inline bool ReCapture(MoveStruct LastMove, MoveStruct + ThisMove) {
    +
     return LastMove.Cpt != 0 && LastMove.Dst == + ThisMove.Dst
    +
    }
    +
    + + +
  • 上一篇 中国象棋程序设计探索():启发算法
  • +
  • 下一篇 中国象棋程序设计探索():并行搜索技术探索
  • +
  • 返 回 象棋百科全书——电脑象棋
  • +
    +
    + + + + + + + + +

    +
    www.elephantbase.net
    +
    + + diff --git a/DOC/eleeye_intro.htm b/DOC/eleeye_intro.htm new file mode 100644 index 0000000..4125f72 --- /dev/null +++ b/DOC/eleeye_intro.htm @@ -0,0 +1,154 @@ + + + + + +中国象棋程序设计探索(一):引言 + + + + +
    +
    +
    中国象棋程序设计探索
    +
    +
     
    +
    +
    黄晨 * 20056月初稿,200712月修订
    +
    +
    ( * 上海计算机博弈研究所,eMailwebmaster@elephantbase.net)
    +
    +
     
    +
    () 引言
    +
     
    +
      20052月我写出了象棋程序ElephantEye的第一个版本(0.90),本来它只是象棋界面ElephantBoard(现改名为象棋巫师)的调试引擎。在设计程序的过程中,我尝试性地加入了很多算法,发现每次改进都能让程序的棋力有大幅度的提高,因此便对象棋程序的算法产生了浓厚的兴趣。到现在我已经陆续对ElephantEye作了无数次加工,使得它的棋力接近了顶尖商业软件的水平,在非商业的象棋程序中,ElephantEye无疑是最强的一个。
    +
      我希望能通过公开源代码的方式,推动中国象棋程序水平的整体发展,然而根据很多网友的反馈意见,发现源代码中的很多部分并不是那么容易理解的,为此我花了大量的时间为源程序加了注释。尽管如此,很多思想还是有必要以文字的形式保留下来,因此我才打算以《中国象棋程序设计探索》为题,写几篇详细介绍ElephantEye算法的连载,希望能让源代码充分发挥它的作用。
    +
      总的来说,对弈程序是个系统工程,它是以下四个系统的有机结合(1) + 棋盘结构,(2) 局面评价,(3) 搜索技术,(4) 其他。以ElephantEye为例,这四个部分在程序中的比例各占25%,也就是说,每个方面都很重要。那么这四个部分应该以什么样的方式逐步建立呢?另一个公开源代码的程序VSCCP(Very Simple Chinese + Chess Program)给出了一个方向,这是本很好的对弈程序设计的入门教材。尽管VSCCP在棋力上还有很大的提升空间,但是它的结构体系是比较完整的,参考下面一组公式,找到有待提升的空间,只要稍作改进就能成为ElephantEye
    +
     
    +
    棋盘结构 = 局面表示 + + 着法移动 + 着法生成 + 特殊局面判断
    +
    局面评价 = 知识 + + 优化的局面表示
    +
    搜索技术 = 完全搜索 + + 静态搜索 + 启发 + 裁剪 + + 选择性延伸 + 置换表 + 残局库 + + 并行技术
    +
    其他 = 开局库 + 时间控制 + 后台思考 + 引擎协议
    +
     
    +
      现在我来回答读者最关心的一个问题——ElephantEye的源程序可以从哪里找到。
    +
    +
      ElephantEye的源程序发布在SourceForgeXiangQi + Wizard项目中,其页面是: +
    +
    +
        http://sourceforge.net/projects/xqwizard/
    +
    +
      ElephantEye的版本改进,实时同步地发布在SourceForgeSVN站点上,获取地址是:
    +
    +
        https://xqwizard.svn.sourceforge.net/svnroot/xqwizard/
    +
    +
      读者可以使用TortoiseSVNSVN客户端程序获取到最新的(跟我完全同步的)代码,TortoiseSVNSourceForge的页面是:
    +
    +
        http://sourceforge.net/projects/tortoisesvn/
    +
    +
     
    +
    +
      本文初稿时ElephantEye的版本为1.0x,现在已经升级到3.1x。尽管初稿完成之后又有几次修订,但本文所涉及的很多细节问题都没有再更新过,所以代码看上去可能面目全非了。但是万变不离其中,读者若是注意代码中的注释,就会发现其基本原理与本文是一致的。
    +
    +
    + + +
  • 上一篇 结语——国际象棋程序剖析
  • +
  • 下一篇 中国象棋程序设计探索():棋盘结构和着法生成器
  • +
  • 返 回 象棋百科全书——电脑象棋
  • +
    +
    + + + + + + + + +

    +
    www.elephantbase.net
    +
    + + diff --git a/DOC/eleeye_parallel.htm b/DOC/eleeye_parallel.htm new file mode 100644 index 0000000..feb00c8 --- /dev/null +++ b/DOC/eleeye_parallel.htm @@ -0,0 +1,364 @@ + + + + + +中国象棋程序设计探索(六):并行搜索技术探索 + + + + +
    +
    +
    中国象棋程序设计探索
    +
    +
     
    +
    +
    黄晨 * 20056月初稿,20075月修订
    +
    +
    ( * 上海计算机博弈研究所,eMailwebmaster@elephantbase.net)
    +
    +
     
    +
    () 并行搜索技术探索
    +
     
    +
    + +
    +
      在阅读本章前,建议读者先阅读象棋百科全书网中《对弈程序基本技术》专题的以下几篇译文: +
    +
      (1) 高级搜索方法——期望窗口(Bruce Moreland)
    +
      (2) 高级搜索方法——主要变例搜索(Bruce Moreland)
    +
    + +
    +
      并行搜索是博弈搜索技术中最复杂的组成部分,这就是ElephantEye没有采用并行技术的原因。尽管如此,笔者还是在这里简单地谈一下对并行搜索的认识。
    +
     
    +
    6.1 并行计算的基本操作
    +
     
    +
      并行计算有两种体系,一是单机体系,即一个程序在单机的多个处理器上运行,简称SMP(对称多处理器),二是分布式体系,即一个程序在多个计算机联成的网络上运行,简称Cluster(计算机集群)。两者最大的区别是,单机体系的多个处理器可以共享存储器(并且共享同一地址的存储单元),而分布式体系则必须通过网络来交换信息(尽管传输速度非常高,通常在1000MBPS以上)
    +
      由于博弈搜索需要用到置换表,所以特别适合以SMP的方式作并行计算。WindowsUNIX等多任务操作系统都有线程(Thread)这个概念,线程是处理器任务的最小单位,一个线程是不可能同时在两个处理器上运行的,建立线程后,操作系统就会自动把新建的线程分配到空闲的处理器上。Windows下建立线程的方法是:
    +
     
    +
    #include <windows.h>
    +
    ……
    +
    DWORD ThreadId;
    +
    CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) + ThreadEntry, (LPVOID) &ArgList, 0, &ThreadId);
    +
     
    +
      UNIX下建立线程的方式是:
    +
     
    +
    #include <pthread.h>
    +
    ……
    +
    pthread_t pthread;
    +
    pthread_attr_t pthread_attr;
    +
    pthread_attr_init(&pthread_attr);
    +
    pthread_attr_setscope(&pthread_attr, PTHREAD_SCOPE_SYSTEM);
    +
    pthread_create(&pthread, &pthread_attr, (void + *(*)(void *)) ThreadEntry, (void *) &ArgList);
    +
     
    +
      这里ThreadEntry仅仅是某个线程的入口,以便整理参数ArgList并且在线程中调用其他函数。例如,一个用递归函数来计算斐波那契数列的并行程序(Windows版本)
    +
     
    +
    static volatile int IdleThreads;
    +
     
    +
    struct ArgListStruct {
    +
     volatile bool Active;
    +
     volatile int Value;
    +
    };
    +
     
    +
    static void *ThreadEntry(ArgListStruct *ArgListPtr);
    +
     
    +
    static int Fibonacci(int Arg) {
    +
     if (Arg < 2) {
    +
      return 1;
    +
     } else {
    +
      return Fibonacci(Arg - 2) + Fibonacci(Arg - 1);
    +
     }
    +
    }
    +
     
    +
    static int FibonacciSMP(int Arg) {
    +
     DWORD ThreadId;
    +
     int Result;
    +
     ArgListStruct ArgList;
    +
     if (Arg < 2) {
    +
      return 1;
    +
     } else {
    +
      if (Arg > 30) {
    +
       if (Decrement(&IdleThreads) < 0) {
    +
        Increment(&IdleThreads);
    +
        return FibonacciSMP(Arg - 2) + FibonacciSMP(Arg + - 1);
    +
       } else {
    +
        ArgList.Value = Arg - 2;
    +
        ArgList.Active = true;
    +
        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) + ThreadEntry, (LPVOID) &ArgList, 0, &ThreadId);
    +
        Result = FibonacciSMP(Arg - 1);
    +
        while (ArgList.Active) {
    +
         Idle();
    +
        }
    +
        return Result + ArgList.Value;
    +
       }
    +
      } else {
    +
       return Fibonacci(Arg - 2) + Fibonacci(Arg - 1);
    +
      }
    +
     }
    +
    }
    +
     
    +
    static void *ThreadEntry(ArgListStruct *ArgListPtr) {
    +
     ArgListPtr->Value = FibonacciSMP(ArgListPtr->Value);
    +
     ArgListPtr->Active = false;
    +
     Increment(&IdleThreads);
    +
     return NULL;
    +
    }
    +
     
    +
      由于Fibonacci()函数的递归形式如同二叉树一样扩展开来,所以当处理器有空闲时,就可以把其中一个分枝交给另一个线程,这个操作称为递归树的分割(Split)。为此程序需要维护变量IdleThreads来判断是否还有空闲的处理器,对该变量的操作必须用Increment()Decrement()函数(Intel处理器的INCDEC原子指令,参阅<x86asm.h>),以保证不会因为有两个线程同时对该变量操作而发生错误,这样的共享变量都应该标记为volatile属性,以避免编译器对这些变量作优化。
    +
      由于线程的维护需要消耗额外的处理器资源,所以并非每个递归结点要作分割的尝试,以上这段程序的核心问题尽管是FibonacciSMP()的递归,但当递归树不太大(参数不超过30)时,调用单纯的递归函数Fibonacci()会得到更高的效率。
    +
     
    +
    6.2 加锁技术和非加锁技术
    +
     
    +
      如果两个线程同时存取同一存储单元,就有可能产生错误,为此必须建立预防错误的机制,通常的措施是加锁(Lock)。一个比较简单的加锁方法是调用函数Exchange()(Intel处理器的XCHG原子指令,参阅<x86asm.h>),因为两个线程的原子语句是不可能同时存取同一存储单元的(正因为如此,处理器对存储单元作INCDECXCHG等操作就需要考虑冲突,所以比较耗时)。在某块共享的存储单元中,第一个变量就是加锁标志,false表示未加锁,true表示加锁。加锁时只要用true和加锁标志交换,换出false就表示加锁成功(原来加锁标志是false,交换后变成true),该线程就获得对共享单元的操作权,换出true则表示加锁失败(原来加锁标志是true,交换后仍旧是true)。对共享单元操作完毕后,再将加锁标志设成false来解锁。
    +
      并行计算的博弈程序会偶尔出现一种情况,即两个线程同时存取同一置换表项,为避免错误可以使用加锁技术:
    +
     
    +
    struct HashRecord {
    +
     volatile int Lock;
    +
     ……
    +
    };
    +
     
    +
    void RecordHash / ProbeHash (……) {
    +
     HashRecord *HashPtr = HashList + ZobristKey % HashSize;
    +
     while (Exchange(&HashPtr->Lock, true)) {
    +
     }
    +
     ……
    +
     HashPtr->Lock = false;
    +
    }
    +
     
    +
      但是,并行国际象棋程序的典范Crafty并没有这样做,而是采用了不加锁的技术,来避免置换表的存取冲突。简而言之,该算法的思想是当置换表项的校验码存取正确时,必须保证局面信息也存取正确(但允许校验码存取不正确,此时探索置换表的例程就会跳过以后的操作而不会引发错误)
    +
      为此Crafty128位置换表项定义为两个64位数W1W2W1是局面信息,而W2存储了W1和校验码的异或值,探测置换表项时,校验码必须由W1W2的异或值求得,这样一旦W1读取错误,就得不到正确的校验码,换句话说,校验码的正确就保证了局面信息的正确。
    +
      Crafty的这种不加锁算法是针对64位处理器而设计的,这种技术当然也适用于32位处理器,而笔者认为32位处理器还有更为简单的处理方式,即设计如下的置换表项:
    +
     
    +
    struct HashRecord {
    +
     long CheckSum1, PositionInfo1, PositionInfo2, CheckSum2;
    +
    };
    +
     
    +
      以上128位的置换表项由432位单元组成,存取置换表时四个单元依次读取或写入。只要位于首尾的校验码都能存取正确,就确保了中间两个局面信息单元的正确。
    +
     
    +
    6.3 搜索树的分割策略
    +
     
    +
      Alpha-Beta搜索是递归式的,因此作并行计算时可以仿照上面提到的Fibonacci()递归的例子,但这里有两个问题需要解决,一是某个子线程产生Beta截断时,如何通知其他子线程中止搜索,二是考虑什么样的结点需要分割,什么样的结点不需要分割。这两个问题都是并行搜索技术的难点,为此Crafty花了大量的代码来解决这些问题,其作者Robert HyattCrafty的并行算法DTS(Dynamic Tree Split,即搜索树的动态分割算法)有专门的介绍。这里笔者只简单地介绍第二个问题,即搜索树的分割策略。
    +
      通常Alpha-Beta搜索树的结点可分为三个类型,即PV结点、Beta结点和Alpha结点,并且有明确的定义——搜索值在窗口(Alpha, Beta)之间的是PV结点(根据最小-最大原理,最佳着法的搜索值必须落在窗口内),搜索值达到或超过Beta(即高出边界)的是Beta结点(仅需要有一个着法超过Beta即可),搜索值未能超过Alpha(即低出边界)的是Alpha结点(所有着法都不能超过Alpha)。由此可以看出,PV结点和Alpha结点都需要搜索所有的着法,而Beta结点在最好的情况下只要搜索一个着法即可。
    +
      如果能在搜索之前预测出结点的类型,就可以决定是否对该结点作分割了,在Crafty及其相关论文中,预测的三类结点命名为PV结点、Cut结点和All结点,分别对应刚才提到的PV结点、Beta结点和Alpha结点。显然,PV结点和All结点是有分割价值的,只要预测正确,这些结点下的全部着法都要搜索,不会因为产生截断而浪费搜索时间,而对Cut结点作分割是没有意义的,因为目前的搜索技术使得Cut结点的第一着法截断率高达80%以上,对结点作分割只会浪费处理器资源。
    +
      至于如何来预测结点类型,在Crafty及其相关论文也有介绍,但是另一个国际象棋程序Fruit则描绘得更加明确,其分类策略是:
    +
      (1) 根结点总是PV结点。
    +
      (2) PV结点采用PVS算法,即第一个着法以后首先尝试零窗口的搜索,由于期望每个着法都低出边界(即子结点都高出边界),所以预测这些零窗口的结点为Cut结点。
    +
      (3) Cut结点和All结点是互相交替的,即Cut结点的子结点是All结点,All结点的子结点是Cut结点,空着裁剪同样这么处理。
    +
      (4) Cut结点的第一个子结点(All结点)如果不能产生截断,就说明原来预测的Cut结点是错的,应该是All结点,那么以后的子结点都预测为Cut结点。换句话说,不管预测是否正确,Cut结点的第一个子结点(不包括空着裁剪)All结点,其余的结点(如果有必要搜索的话)仍旧是Cut结点。
    +
      (5) PV结点不采用各种形式的向前裁剪(Null-MoveHistoryFutility)
    +
      由于Fruit不支持并行搜索,所以Cut结点和All结点没有区别,但给并行搜索留下了伏笔。
    +
      我们关注的是PV结点的分割,显然PV结点是有必要分割的,因为分割越早,新的线程所处理的搜索树就越大,并行效率就越高。(注意Fibonacci()递归的例子,为保证效率只有参数充分大时才会分割,Alpha-Beta递归也一样。)但是PV结点的分割会遇到一个问题——窗口宽度可能会变窄,如果让所有的子结点都作相同窗口的搜索,就无法体现Alpha-Beta算法的效率了。为此,Crafty对根结点的迭代加深过程用了期望窗口(Aspiration Window),即让所有的子结点都搜索一个较窄的窗口,这要比直接分割(-MATE_VALUE, MATE_VALUE)有效得多。而另一个极端的做法是MTD(f)的零窗口,根结点从一开始就预测为CutAll结点,即可实施有效的分割,这就是MTD(f)算法在并行计算上的优势。
    +
    + + +
  • 上一篇 中国象棋程序设计探索():克服水平线效应
  • +
  • 下一篇 中国象棋程序设计探索():开局库
  • +
  • 返 回 象棋百科全书——电脑象棋
  • +
    +
    + + + + + + + + +

    +
    www.elephantbase.net
    +
    + + diff --git a/DOC/eleeye_ponder.htm b/DOC/eleeye_ponder.htm new file mode 100644 index 0000000..5fa8317 --- /dev/null +++ b/DOC/eleeye_ponder.htm @@ -0,0 +1,143 @@ + + + + + +中国象棋程序设计探索(八):后台思考和时间策略 + + + + +
    +
    +
    中国象棋程序设计探索
    +
    +
     
    +
    +
    黄晨 * 20056月初稿,20075月修订
    +
    +
    ( * 上海计算机博弈研究所,eMailwebmaster@elephantbase.net)
    +
    +
     
    +
    () 后台思考和时间策略
    +
     
    +
      在阅读本章前,建议读者先阅读象棋百科全书网中《对弈程序基本技术》专题的以下几篇译文:
    +
      (1) 其他策略——后台思考(Bruce Moreland)
    +
     
    +
    8.1 后台思考
    +
     
    +
      UCCI协议不需要对后台思考作特别处理,如果引擎接收了指令“go ponder ...”,那么搜索过程中时钟暂时不起作用,直到收到“ponderhit”指令后才启用时钟。因此,后台思考实际上相当于无限制的思考。
    +
     
    +
    8.2 时间策略
    +
     
    +
      时间策略在各个象棋程序中差异很大,有的程序根本没有时间策略,只能设定固定的搜索深度,或者在固定的时间中止思考,例如浅红象棋协议目前就没有时间策略。UCCI协议可以把时限规则告诉引擎,由引擎自动分配时间,时限规则可以是以下两种:
    +
      (1) 时段制,即在限定时间内走完规定的步数,用“go time <time> movestogo + <moves_to_go>”命令;
    +
      (2) 加时制,即在限定时间内走完整盘棋,但每步会加上几秒,用“go time <time> increment + <increment>”命令。
    +
      ElephantEye的时间策略由<search.h>里的SearchMain()函数来处理,不管处理哪个规则,都会分配一个合适的时间(ProperTime)用来走棋,这个时间是这样计算的:
    +
      (1) 时段制:分配时间 + = 剩余时间 / 要走的步数;
    +
      (2) 加时制:分配时间 + = 每步增加的时间 + + 剩余时间 / 20 (即假设棋局会在20步内结束)
    +
     
    +
      在每次迭代加深的过程中,一个深度结束后,即将进行更深一层的搜索时,需要根据用时来判断是否终止搜索,判断的依据通常有:
    +
      (1) 一般情况下,用时超过分配时间的一半,就要终止搜索;
    +
      (2) 如果连续 + n 次的迭代加深返回同样的着法,那么用时超过分配时间的1/4时就可以终止搜索,因为更深一层的搜索很有可能还是得到同样的着法;
    +
      (3) 如果当前深度得到的分值比上一深度到的分值低很多,那么用时超过分配时间才终止搜索(仅仅超过分配时间的一半是不够的),期待搜索得更深以挽回劣势。
    +
      另外,搜索程序还会确定一个最大搜索时间(通常是剩余时间的一半),如果搜索花费的时间超过预先设定的最大搜索时间,则强行中止搜索,这个检测过程由Interrupt()函数来完成。
    +
     
    +
    8.3 搜索杀棋的策略
    +
     
    +
      ElephantEye没有专门搜索杀棋的功能,如果存在杀棋的话(不管是杀死对方还是被对方杀死),会在搜索中自动找到。是否能找到杀棋和搜索深度有关,某一深度下找不到杀棋,但深一层搜索就可能找到;但和一般局面不同的是,如果一定深度能找到杀棋,那么再深的深度会得到同样的结果。因此,如果找到杀棋,那么程序要使用不同的策略。ElephantEye处理杀棋局面时,用到以下几个策略:
    +
      (1) 置换表的存取策略,前面曾经介绍过,如果置换表中存储的某个局面已被确认找到杀棋,那么探测到这样的局面时就不需要考虑深度条件。
    +
      (2) 根结点做迭代加深时,找到杀棋后搜索就立即停止。ElephantEye为杀局设定了边界WIN_VALUE,其值略比MATE_VALUE小一些,局面分值在区间(-WIN_VALUE, WIN_VALUE)以外,就说明该局面有杀棋。
    +
      (3) 如果根结点的所有着法中,除了一个着法可以支撑(即分值大于-WIN_VALUE)以外,其余着法都会输掉(即分值都小于-WIN_VALUE),那么应该立即返回这个唯一着法。
    +
      最后一点是ElephantEye最有特色之处,这就是说,当迭代加深过程中遇到这种情况时,就没有必要做更深的搜索了。在对某个深度的完全搜索完成后,对最佳着法设为禁着,再对根结点作一次搜索,如果分值小于-WIN_VALUE,则说明除这个最佳着法以外其余着法都会输掉,这种搜索称为“唯一着法检验搜索”,<search.cpp>中需要用变量bCheckUnique来控制。启用唯一着法检验搜索时,有两个地方要注意:
    +
      (1) 使用零窗口的技巧,即用(-WIN_VALUE, 1 - WIN_VALUE)窗口作搜索,以减少搜索的结点数;
    +
      (2) 根结点不记录到置换表中。
    +
    + + +
  • 上一篇 中国象棋程序设计探索():开局库
  • +
  • 下一篇 中国象棋程序设计探索():局面评价函数
  • +
  • 返 回 象棋百科全书——电脑象棋 +
  • +
    +
    + + + + + + + + +

    +
    www.elephantbase.net
    +
    + + diff --git a/DOC/eleeye_search.htm b/DOC/eleeye_search.htm new file mode 100644 index 0000000..1781726 --- /dev/null +++ b/DOC/eleeye_search.htm @@ -0,0 +1,327 @@ + + + + + +中国象棋程序设计探索(三):搜索和置换表 + + + + +
    +
    +
    中国象棋程序设计探索
    +
    +
     
    +
    +
    黄晨 * 20056月初稿,20075月修订
    +
    +
    ( * 上海计算机博弈研究所,eMailwebmaster@elephantbase.net)
    +
    +
     
    +
    () 搜索与置换表
    +
     
    +
      在阅读本章前,建议读者先阅读象棋百科全书网中《对弈程序基本技术》专题的以下几篇译文:
    +
      (1) 基本搜索方法——简介()(David Eppstein)
    +
      (2) 基本搜索方法——简介()(David Eppstein)
    +
      (3) 基本搜索方法——简介()(David Eppstein)
    +
      (4) 基本搜索方法——最小-最大搜索(Bruce Moreland)
    +
      (5) 基本搜索方法——Alpha-Beta搜索(Bruce Moreland)
    +
      (6) 基本搜索方法——迭代加深(Bruce Moreland)
    +
      (7) 基本搜索方法——置换表(Bruce Moreland)
    +
      (8) 高级搜索方法——简介()(David Eppstein)
    +
      (9) 高级搜索方法——搜索的不稳定性(Bruce Moreland)
    +
      (10) 其他策略——胜利局面(David Eppstein)
    +
     
    +
    3.1 搜索技术概述
    +
     
    +
      搜索算法是象棋程序的核心算法,在众多搜索算法中,如何选择适合自己的算法,并有效地把它们组合起来,是决定搜索效率的关键所在。要做好这点,首先必须明确搜索的概念,把各种搜索算法作一下分类。
    +
      象棋程序的搜索算法都是基于“最小-最大”策略的,因此衍生出以Alpha-Beta为基础的完全搜索算法以及带裁剪的搜索算法。尽管Alpha-Beta算法也有裁剪的过程,但是这种裁剪被证明是绝对可靠的,没有无负面作用,即通常所说的“截断”(Cut-off),它属于申朗所说的A策略。
    +
      我们现在所说的“裁剪”(Pruning),特指“向前裁剪”(Forward Pruning),即需要承担风险地对某些线路作的裁剪,也就是申朗所说的B策略。尽管它是完全搜索算法的对立面,但为了克服完全搜索中的“水平线效应”(Horizon Effect),它是程序中必不可少的部分。把裁剪反过来,对某些重要线路进行“延伸”(Extension),这种思想和裁剪有异曲同工之妙。
    +
      如今,“带置换表的启发式Alpha-Beta搜索”成了象棋程序的主流,这种思想强调对着法排序的重要性,排序着法是由“启发”(Heuristic)算法来完成的,它同“置换表”(Transposition Table)一起,使搜索效率有大幅度的提高。
    +
      因此,搜索算法大致可以分为以下四类:
    +
      (1) 完全搜索算法,即Alpha-Beta搜索及其变种,诸如期望窗口、PVSMTD(f)等;
    +
      (2) 启发算法,对着法顺序进行优化,尽可能地提高Alpha-Beta搜索的效率,中国象棋中的启发算法有置换表启发、吃子启发、杀手着法启发和历史表启发等;
    +
      (3) 裁剪算法,运用棋类知识略去对某些着法的搜索,以提高搜索深度,中国象棋中最常用的裁剪算法是空着裁剪,当然还有其他方法。
    +
      (4) 置换表。
    +
      以上算法中,置换表被独立归为一类,因为它不但功能特殊,而且值得研究问题最多。置换表的初衷是利用置换现象来减少搜索(即置换裁剪,属于裁剪算法的范畴),然而在象棋的中局阶段,置换现象并不那么普遍,因此它的主要功效在于启发(即置换表启发,属于启发算法的范畴)。另外,置换现象会导致搜索的不稳定性,因而会产生很多负面作用(毕竟是裁剪的一种形式),而要彻底研究清楚并非那么容易。
    +
     
    +
    3.2 超出边界的Alpha-Beta搜索
    +
     
    +
      置换表的大部分问题出在边界上,直到目前笔者还无法彻底明白该如何设置边界,因此想把这个问题留给读者。首先我们从不带置换表的超出边界(Fail-Soft)Alpha-Beta搜索说起:
    +
     
    +
    int AlphaBeta(int Alpha, int Beta, int Depth) {
    +
     if (GameOver() || Depth <= 0) {
    +
      Value = Evaluate();
    +
      // if (Value >= Beta) + return Beta;
    +
      // if (Value <= Alpha) + return Alpha;
    +
      return Value;
    +
     }
    +
     Best = -MaxValue;
    +
     GenMoves();
    +
     while (MovesLeft()) {
    +
      MakeNextMove();
    +
      Value = -AlphaBeta(-Beta, -Alpha, Depth - 1);
    +
      UndoMakeMove();
    +
      if (Value >= Beta) {
    +
       // return Beta;
    +
       return Value;
    +
      }
    +
      if (Value > Best) {
    +
       Best = Value;
    +
       if (Value > Alpha) {
    +
        Alpha = Value;
    +
       }
    +
      }
    +
     }
    +
     // return Alpha;
    +
     return Best;
    +
    }
    +
     
    +
      以上代码中,蓝色的被注释掉的部分是不超出边界的Alpha-Beta搜索,红色的代码就是超出边界的Alpha-Beta搜索了。如果不使用置换表,那么这种改动和原来没有区别,但是在置换表的作用下,情况就会有微妙的变化。探索置换表的形式(是否超出边界)应该与Alpha-Beta的形式保持一致:
    +
     
    +
    int ProbeHash(int Alpha, int Beta, int Depth) {
    +
     ……
    +
     if (Hash.Flag == HASH_BETA) {
    +
      if (Hash.Value >= Beta) {
    +
       // return Beta;
    +
       return Hash.Value;
    +
      }
    +
     } else if (Hash.Flag == HASH_ALPHA) {
    +
      if (Hash.Value <= Alpha) {
    +
       // return Alpha;
    +
       return Hash.Value;
    +
      }
    +
     } else if (Hash.Flag == HASH_PV) {
    +
      // if (Hash.Value >= Beta) + return Beta;
    +
      // if (Hash.Value <= Alpha) + return Alpha;
    +
      return Hash.Value;
    +
     }
    +
    }
    +
     
    +
      同样的问题出现在记录置换表时:
    +
     
    +
    int AlphaBeta(int Alpha, int Beta, int Depth) {
    +
     ……
    +
     while (……) {
    +
      ……
    +
      if (Value >= Beta) {
    +
       // RecordHash(Beta, HASH_BETA, + Depth);
    +
       RecordHash(Value, HASH_BETA, + Depth);
    +
       return ……;
    +
      }
    +
      ……
    +
     }
    +
     // RecordHash(Alpha, HashFlag, + Depth);
    +
     RecordHash(Best, HashFlag, Depth);
    +
     return ……;
    +
    }
    +
     
    +
      在带置换表的Alpha-Beta搜索中,到底超出边界(Fail-Soft)和不超出边界(Fail-Hard)哪个效率更高,到现在为止还很难有定论。从上面的代码中可以看出,采用超出边界算法时,置换表的边界变窄了,即Beta结点的可能值从(Beta, MATE_VALUE缩小为(Value, MATE_VALUE),本来低于Beta才会命中的结点,现在只要低于Value就能命中了。但是很多试验表明,某些局面在使用了超出边界算法时,搜索的结点数反而增加了。因为置换现象会产生搜索的不稳定性,置换表命中率高也会导致不稳定性的增强,搜索的结点数增加就是其中一个表现。
    +
     
    +
    3.3 胜利局面的特殊处理
    +
     
    +
      胜利局面就是程序能搜索到的有杀局的局面,它具有特殊的分值——最大值减去“根结点”到“将死结点”的步数(简称杀棋步数或DTM)。而当这个数值记录到置换表的时候,就必须转化为最大值减去“当前结点”到“将死结点”的步数。
    +
      除此以外杀局还有一个显著的特点——对一个局面进行某一深度的搜索后,如果已经证明它是杀局,那么就不必进行更深层次的搜索了。利用这点,可以改进探索置换表的程序,提高置换表的命中率,从而提高搜索效率:
    +
     
    +
    const int WIN_VALUE = MATE_VALUE - 100;
    +
     
    +
    int ProbeHash(int Alpha, int Beta, int Depth) {
    +
     ……
    +
     MateNode = false;
    +
     if (Hash.Value > WIN_VALUE) {
    +
      Hash.Value -= Ply;
    +
      MateNode = true;
    +
     } else if (Hash.Value < -WIN_VALUE) {
    +
      Hash.Value += Ply;
    +
      MateNode = true;
    +
     }
    +
     if (MateNode || Hash.Depth > Depth) {
    +
      ……
    +
     }
    +
    }
    +
     
    +
      这样做似乎在中局阶段并不能起到很好的效果,但是在处理杀局和残局时,搜索的结点数大幅度降低了,ElephantEye在使用了这种方法以后,杀局和残局的处理能力有了很大的提高。
    +
     
    +
    3.4 长将禁手局面的特殊处理
    +
     
    +
      由于单方面长将不变作负的规则,从表面上看,如果发生这种情况就应该,给予-MATE_VALUE的值,再根据杀棋步数作调整。但是由于长将判负并不是对某个单纯局面的评分,而是跟路径有关的,所以使用置换表时就会产生非常严重的后果,这也是搜索不稳定性的一个来源。简而言之,即同一局面由于形成路径不同而有不同的分值。
    +
      最简单的解决办法就是不要把“利用长将判负策略搜索到的局面”记录到置换表中,为此把长将判负的局面定为BAN_VALUE,定义为MATE_VALUE - 50,再根据杀棋步数作调整。考虑到搜索层数通常不会超过50层,因此如果某个局面分值在WIN_VALUE(MATE_VALUE - 100)BAN_VALUE之间,那么这个局面就是“利用长将判负策略搜索到的局面”,不记录到置换表中。根据这个思路,代码就应该写成: +
    +
     
    +
    const int BAN_VALUE = MATE_VALUE - 50;
    +
     
    +
    void RecordHash(int HashFlag, int Value, int Depth) {
    +
     if (Value > WIN_VALUE) {
    +
      if (Ply > 0 && Value <= BAN_VALUE) {
    +
       return;
    +
      }
    +
      Value += Ply;
    +
     } else if (Value < -WIN_VALUE) {
    +
      if (Ply > 0 && Value >= -BAN_VALUE) {
    +
       return;
    +
      }
    +
      Value -= Ply;
    +
     }
    +
     ……
    +
    }
    +
     
    +
      之所以允许Ply == + 0的结点写入置换表,是因为这样的结点是根结点,ElephantEye需要依靠它来获得最佳着法。
    +
     
    +
    3.5 + 置换表的覆盖策略
    +
     
    +
      置换表的覆盖策略通常有两种:深度优先和始终覆盖。ElephantEye以前几个版本只用了深度优先的策略,因此代码并不复杂,并且在搜索时间不太长的情况下,这种策略非常有效。但当置换表接近填满的时候,把深度优先和始终覆盖两种策略结合起来,效果就会很明显了。那么如何把这两种覆盖策略结合起来呢?
    +
      ElephantEye参考了国际象棋程序Fruit的做法,采用多层结构的置换表。简而言之,一个m层的置换表有nHash位置,每个Hash位置记录m个局面,那么整个Hash表可记录m × n个局面信息。记录一个局面时,找到它对应的Hash位置中的m个局面,不考虑该局面本身的搜索深度,而直接覆盖深度最小的那个局面。这种策略的本质就是将深度优先和始终覆盖结合起来——原先深度最小的那个局面是始终覆盖的,而其余m - 1个搜索深度更深的局面则被保留下来。
    +
    + + +
  • 上一篇 中国象棋程序设计探索():棋盘结构和着法生成器
  • +
  • 下一篇 中国象棋程序设计探索():启发算法
  • +
  • 返 回 象棋百科全书——电脑象棋
  • +
    +
    + + + + + + + + +

    +
    www.elephantbase.net
    +
    + + diff --git a/DOC/eleeye_struct.htm b/DOC/eleeye_struct.htm new file mode 100644 index 0000000..735fd6a --- /dev/null +++ b/DOC/eleeye_struct.htm @@ -0,0 +1,1481 @@ + + + + + +中国象棋程序设计探索(二):棋盘结构和着法生成器 + + + + +
    +
    +
    中国象棋程序设计探索
    +
    +
     
    +
    +
    黄晨 * 20056月初稿,200712月修订
    +
    +
    ( * 上海计算机博弈研究所,eMailwebmaster@elephantbase.net)
    +
    +
     
    +
    () 棋盘结构和着法生成器
    +
     
    +
      在阅读本章前,建议读者先阅读象棋百科全书网中《对弈程序基本技术》专题的以下几篇译文:
    +
      (1) 数据结构——简介(David Eppstein)
    +
      (2) 数据结构——位棋盘(James Swafford)
    +
      (3) 数据结构——旋转的位棋盘(James Swafford)
    +
      (4) 数据结构——着法生成器(James Swafford)
    +
      (5) 数据结构——0x88着法产生方法(Bruce Moreland)
    +
      (6) 数据结构——Zobrist键值(Bruce Moreland)
    +
     
    +
    2.1 局面和着法的表示
    +
     
    +
      局面是象棋程序的核心数据结构,除了要包括棋盘、棋子、哪方要走、着法生成的辅助结构、Zobrist键值等,还要包含一些历史着法,来判断重复局面。ElephantEye的局面结构很庞大(<position.h>),其中大部分存储空间是用来记录历史局面的。
    +
     
    +
    struct PositionStruct {
    +
     ……
    +
     int nMoveNum;
    +
     MoveStruct mvMoveList[MAX_MOVE_NUM]; +  // MAX_MOVE_NUM = 256
    +
     unsigned char ucRepHash[REP_HASH_LEN]; + // REP_HASH_LEN = 1024
    +
     ……
    +
    }
    +
     
    +
      其中MoveStruct这个结构记录了四个信息:(1) 着法的起始格(Src)(2) 着法的目标格(Dst)(3) 着法吃掉的棋子(Cpt)(4) 着法是否将军(Chk)。有意思的是,每个部分都只占一个字节,后两个部分(CptChk)与其说有特殊作用,不如说是为了凑一个32位整数。在MoveStruct出现的很多地方(置换表、杀手着法表、着法生成表)里,这两项都是没作用的,而只有在PositionStruct结构的记录历史着法的堆栈中才有意义。
    +
      Cpt一项主要用在撤消着法上,它可以用来还原被吃的棋子,而Chk一项则可以记录当前局面是否处于将军状态。ElephantEye是用MakeMove()函数来走棋的,每走完一步棋就做两次将军判断:第一次判断走完子的一方是否被将军,即Checked(sdPlayer),如果被将则立即撤消着法,并返回走子失败的信息;第二次判断要走的一方是否被将军,由于交换了走子方(即执行了sdPlayer = 1 - + sdPlayer),所以仍旧是Checked(sdPlayer),如果被将则Chk置为TRUE,这个着法被压入历史着法堆栈。因此LastMove().Chk就表示当前局面是否被将军。
    +
     
    +
    2.2 循环着法的检测
    +
     
    +
      CptChk的另一个作用就是判断循环着法:ElephantEye判断循环着法时,依次从堆栈顶往前读,读到吃过子的着法(Cpt不为零)就结束;而是否有单方面长将的情况,则是通过每个着法的Chk一项来判断的。
    +
      在循环着法的检测中,我们感兴趣的不是CptChk,而是RepHash结构,这是一个微型的置换表,用来记录历史局面。ElephantEye在做循环着法的判断这之前,先去探测这个置换表,如果命中置换表,则说明可能存在重复局面(由于置换表可能有冲突,所以只是有这个可能),因而做循环检测(即比较当前Zobrist键值与前2步、4步、6步等等的一系列局面是否一致);如果没有命中则肯定没有重复局面。
    +
     
    +
    2.3 棋盘-棋子联系数组
    +
     
    +
      众所周知,棋盘的表示有两种方法。一是做一个棋盘数组(例如Squares[10][9]),每个元素记录棋子的类型(包括空格);二是做一个棋子数组(例如Pieces[2][16]),每个元素记录棋子的位置(包括被吃的状态)。如果一个程序同时使用这两个数组,那么着法生成的速度就可以快很多。这就是“棋盘-棋子联系数组”,它的技术要点是:
    +
      (1) 同时用棋盘数组和棋子数组表示一个局面,棋盘数组和棋子数组之间可以互相转换。
    +
      (2) 随时保持这两个数组之间的联系,棋子移动时必须同时更新这两个数组。
    +
      根据这两个原则,棋盘-棋子联系数组可以定义为:
    +
     
    +
    struct PositionStruct {
    +
     int Squares[90];
    +
     int Pieces[32];
    +
    };
    +
     
    +
      在棋盘上删除一个棋子,需要做两个操作(分别修改棋盘数组和棋子数组)。同样,添加一个棋子时也需要两个操作。执行一个着法时有三个步骤:
    +
      (1) 如果目标格上已经有棋子,就要先把它从棋盘上拿走(吃子的过程)
    +
      (2) 把棋子从起始格上拿走;
    +
      (3) 把棋子放在目标格上。
    +
      ElephantEye用一个函数MovePiece()来完成这项任务,它除了修改棋盘数组和棋子数组外,还修改Zobrist键值、位行和位列等信息。
    +
      “棋盘-棋子联系数组”最大的优势是:移动一步只需要有限的运算。对于着法产生过程,可以逐一查找棋子数组,如果该子没有被吃掉,就产生该子的所有合理着法,由于需要查找的棋子数组的数量(每方只有16个棋子能走)比棋盘格子的数量(90个格子)少得多,因此联系数组的速度要比单纯的棋盘数组快很多。可以说,“棋盘-棋子联系数组”是所有着法生成器的基础,位行和位列、位棋盘等其他方法都只是辅助手段。
    +
     
    +
    2.4 扩展的棋盘数组和棋子数组
    +
     
    +
      如今,很少有程序使用Squares[90]Pieces[32]这样的数组了,浪费一些存储空间以换取速度是流行的做法,例如ElephantEye就用了ucpcSquares[256]ucsqPieces[48]。把棋盘做成16x16的大小,得到行号和列号就可以用16除,这要比用910除快得多。16x16的棋盘还有更大的好处,它可以防止棋子走出棋盘边界。
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    000102030405060708090a0b0c0d0e0f
    101112131415161718191a1b1c1d1e1f
    202122232425262728292a2b2c2d2e2f
    303132333435363738393a3b3c3d3e3f
    404142434445464748494a4b4c4d4e4f
    505152535455565758595a5b5c5d5e5f
    606162636465666768696a6b6c6d6e6f
    707172737475767778797a7b7c7d7e7f
    808182838485868788898a8b8c8d8e8f
    909192939495969798999a9b9c9d9e9f
    a0a1a2a3a4a5a6a7a8a9aaabacadaeaf
    b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
    c0c1c2c3c4c5c6c7c8c9cacbcccdcecf
    d0d1d2d3d4d5d6d7d8d9dadbdcdddedf
    e0e1e2e3e4e5e6e7e8e9eaebecedeeef
    f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff
    +
    + +
    +
      在中国象棋里,短程棋子(短兵器)指的是除车和炮以外的其他棋子,它们的着法都有固定的增量(行的增量,列的增量),因此处理起来非常简单,也是着法生成技术的基础。例如马有8个着法,增量分别是±0x0e、±0x12、±0x1f和±0x21,红方的过河兵有3个着法,增量分别是-0x10和±0x01
    +
      16x16的扩展棋盘如上图所示,底色是红色的格子都被标上“出界”的标记,目标格在这些格子上就说明着法无效。这样,马的着法产生就非常简单了:
    +
     
    +
    const int cnKnightMoveTab[8] = {-0x21, -0x1f, -0x12, -0x0e, + +0x0e, +0x12, +0x1f, +0x21};
    +
    const int cnHorseLegTab[8] = {-0x10, -0x10, -0x01, +0x01, + -0x01, +0x01, +0x10, +0x10};
    +
     
    +
    for (i = MyFirstHorse; i < MyLastHorse; i ++) {
    +
     // 在ElephantEye的Pieces[48]中,红方的MyFirstHorse为21,MyLastHorse为22。
    +
     SrcSq = ucsqPieces[i];
    +
     if (SrcSq != 0) {
    +
      for (j = 0; j < 8; j ++) {
    +
       DstSq = SrcSq + cnKnightMoveTab[j];
    +
       LegSq = SrcSq + cnHorseLegTab[j];
    +
       if (cbcInBoard[DstSq] && (ucpcSquares[DstSq] + & MyPieceMask) == 0 && ucpcSquares[LegSq] == + 0) {
    +
        MoveList[MoveNum].Src = SrcSq;
    +
        MoveList[MoveNum].Dst = DstSq;
    +
        MoveNum ++;
    +
       }
    +
      }
    +
     }
    +
    }
    +
     
    +
      上面的代码是着法生成器的典型写法,用了两层循环,第一层循环用来确定要走的棋子,第二层循环用来确定棋子走到的目标格。如果要加快程序的运行速度,第二个循环可以拆成顺序结构。这个代码还加入了蹩马腿的判断,马腿的位置增量由ccHorseLegTab[j]给出。
    +
      其它棋子的着法也同样处理,只要注意帅()和仕()InBoard[DstSq]改为InFort[DstSq]就可以了。而对于兵和象等需要考虑是否能过河的棋子,判断是否过河的方法非常简单:红方是(SrcSq/DstSq & 0x80) != 0,黑方是(SrcSq/DstSq & 0x80) == 0
    +
      Pieces[48]这个扩展的棋子数组比较难以理解,实际上用了“屏蔽位”的设计,即1位表示红子(16)1位表示黑子(32)。因此016没有作用,1631代表红方棋子(16代表帅,1718代表仕,依此类推,直到2731代表兵)3247代表黑方棋子(在红方基础上加16)。这样,棋盘数组Squares[256]中的每个元素的意义就明确了,0代表没有棋子,1631代表红方棋子,3247代表黑方棋子。这样表示的好处就是:它可以快速判断棋子的颜色,(Piece & 16)可以判断是否为红方棋子,(Piece & 32)可以判断是否为黑方棋子。
    +
      “屏蔽位”的设计不仅仅限制在判断红方棋子还是黑方棋子,如果在棋子数组上再多加7个屏蔽位,就可以对每个兵种作快速判断,例如判断是否是红兵,不需要用(Piece >= 27 && Piece + <= 31),而只要简单的(Piece & WhitePawnBitMask)即可。这样的话,棋子数组的大小就增加到2^12=4096个了,其中9个屏蔽位,还有3位表示同兵种棋子的编号(注意兵有5个,所以必须占据3)。事实上,确实有象棋程序是使用Pieces[4096]的。
    +
     
    +
    2.5 着法预生成数组
    +
     
    +
      上面提到的着法生成技术,在速度上并不是最快的。我们仍旧以马的着法为例,在很多情况下,马会处于棋盘的边缘,所以往往着法只有很少,而并不需要对每个马都作8次是否出界的判断。因此,对于每个短程子力,都给定一个[256][4][256][9]不等的数组,它们保存着棋子可以到达的绝对位置,这些数组称为“着法预生成数组”。例如,ElephantEye里用了ucsqKnightMoves[256][12]ucsqHorseLegs[256][8],前一个数组的第二个维度之所以大于8,是因为着法生成器依次读取数组中的值,读到0就表示不再有着法(12则是为了对齐地址)。程序基本上是这样的:
    +
     
    +
    for (i = MyFirstHorse; i <= MyLastHorse; i ++) {
    +
     SrcSq = ucsqPieces[i];
    +
     if (SrcSq != 0) {
    +
      j = 0;
    +
      DstSq = ucsqKnightMoves[SrcSq][j];
    +
      while (DstSq != 0) {
    +
       LegSq = ucsqHorseLegs[SrcSq][j];
    +
       if (!(ucpcSquares[DstSq] & MyPieceMask) + && ucpcSquares[LegSq] == 0) {
    +
        MoveList[MoveNum].Src = SrcSq;
    +
        MoveList[MoveNum].Dst = DstSq;
    +
        MoveNum ++;
    +
       }
    +
       j ++;
    +
       DstSq = ucsqHorseMoves[SrcSq][j];
    +
      }
    +
     }
    +
    }
    +
     
    +
      和前一个程序一样,这个程序也同样用了两层循环,不同之处在于第二个循环读取的是着法预生成数组,DstSqucsqHorseMoves[256][12]中读出,LegSqucsqHorseLegs[256][8]中读出。
    +
     
    +
    2.6 位行和位列
    +
     
    +
      车和炮的着法分为吃子和不吃子两种,这两种着法生成器原则上是分开的,因此分为车炮不吃子、车吃子和炮吃子三个部分。不吃子的着法可以沿着上下左右四条射线逐一生成(即并列做4个循环)。我们感兴趣的是吃子的着法,因为静态搜索只需要生成这种着法,能否不用循环就能做到?ElephantEye几乎就做到了。
    +
      “位行”和“位列”是目前比较流行的着法生成技术,但仅限于车和炮的着法,它是否有速度上的优势还很难说,但是设计程序时可以减少一层循环,这个思想就已经比较领先了。以“位”的形式记录棋盘上某一行所有的格子的状态(仅仅指是否有子),就称为“位行”(BitRank),与之对应的是“位列”(BitFile),棋盘结构应该包含10个位行和9个位列,即:
    +
     
    +
    struct PositionStruct {
    +
     ……
    +
     unsigned short wBitFiles[16];
    +
     unsigned short wBitRanks[16];
    +
     ……
    +
    };
    +
     
    +
      值得注意的是,它仅仅是棋盘的附加信息,“棋盘-棋子联系数组”仍旧是必不可少的。它的运作方式有点和“棋盘-棋子联系数组”类似:
    +
      (1) 同时用位行数组和位列数组表示棋盘上的棋子分布信息,位行数组和位列数组之间可以互相转换; +
    +
      (2) 随时保持这两个数组之间的联系,棋子移动时必须同时更新这两个数组。
    +
      因此,移走或放入一颗棋子时,必须在位行和位列上置位:
    +
     
    +
    void AddPiece(int Square, int Piece) + {
    +
     ……
    +
     x = Square % 16;
    +
     y = Square / 16;
    +
     wBitFiles[x] = 1 << (y - 3);
    +
     wBitRanks[y] = 1 << (x - 3);
    +
     ……
    +
    }
    +
     
    +
      车和炮是否能吃子(暂时不管吃到的是我方棋子还是敌方棋子),只取决于它所在的行和列上的每个格子上是否有棋子,而跟棋子的颜色和兵种无关,因此这些信息完全反映在位行和位列中。预置一个“能吃到的格子”的数组,以位行或位列为指标查找数组,就可以立即知道车或炮能吃哪个子了。预置数组到底有多大呢?
    +
     
    +
    // 某列各个位置的车或炮(10)在各种棋子排列下(1024)能走到的最上边或最下边的格子
    +
    unsigned char ucsqFileMoveNonCap[10][1024][2];  // + 不吃子
    +
    unsigned char ucsqFileMoveRookCap[10][1024][2];  // + 车吃子
    +
    unsigned char ucsqFileMoveCannonCap[10][1024][2]; // + 炮吃子
    +
    // 某列各个位置的车或炮(9)在各种棋子排列下(512)能走到的最左边或最右边的格子
    +
    unsigned char ucsqRankMoveNonCap[9][512][2];
    +
    ……
    +
     
    +
      数组中的值记录的是目标格子的偏移值,即相对于该行或列第一个格子的编号。产生吃子着法很简单,以车吃子位例:
    +
     
    +
    for (i = MyFirstRook; i <= MyLastRook; i ++) {
    +
     SrcSq = ucsqPieces[i];
    +
     if (SrcSq != -1) {
    +
      x = SrcSq % 16;
    +
      y = SrcSq / 16;
    +
      DstSq = ucsqFileMoveRookCap[y - 3][wBitFiles[x]][0]; + // 得到向上吃子的目标格
    +
      if (DstSq != 0) {
    +
       DstSq += x * 16; // 注意:第x列的第一个格子总是x + * 16。
    +
       MoveList[MoveNum].Src = SrcSq;
    +
       MoveList[MoveNum].Dst = DstSq;
    +
       MoveNum ++;
    +
      }
    +
      …… // 再把FileMoveRookCap[...][...][0]替换成[...][...][1],得到向下吃子的着法
    +
      DstSq = ucsqRankMoveRookCap[x - 3][wBitRanks[y]][0]; + // 得到向左吃子的目标格
    +
      if (DstSq != 0) {
    +
       DstSq += y; // 注意:第y行的第一个格子总是y。
    +
       MoveList[MoveNum].Src = SrcSq;
    +
       MoveList[MoveNum].Dst = DstSq;
    +
       MoveNum ++;
    +
      }
    +
      …… // 再把RankMoveRookCap[...][...][0]替换成[...][...][1],得到向右吃子的着法
    +
     }
    +
    }
    +
     
    +
    2.7 着法合理性的判断
    +
     
    +
      ElephantEye搜索每个结点时,着法都有四个来源:(1) 置换表,(2) 吃子着法生成器,(2) 杀手着法表,(3) 不吃子着法生成器。这四种来源分别代表了四种启发式算法:(1) 置换表启发,(2) 吃子启发,(3) 杀手着法启发,(4) 历史表启发,这会在以后的章节中介绍。
    +
      我们感兴趣的是杀手着法,它是以前搜索过的局面遗留下来的着法,当前的局面如果要使用这些着法,只需要做合理性的判断就可以了,如果杀手着法能产生截断,那么着法生成就没有必要了。因此,如何快速判断着法合理性,其意义可能比着法生成器还大。
    +
      ElephantEye判断着法合理性的程序包含在<position.cpp>中,它分为三个步骤:
    +
      (1) 判断棋子是否在棋盘上存在,如果不存在那么肯定不是合理着法;
    +
      (2) 判断是否吃到自己一方的棋子,吃到自己棋子的着法肯定是不是合理着法;
    +
      (3) 分兵种作额外的判断。
    +
      我们感兴趣的是相()马车炮四子的判断,其中相()的判断最简单,只需要满足3个条件:
    +
      (1) 走成象步,ElephantEye里用了一个ccLegalMoveTab的数组;
    +
      (2) 没有过河,即((SrcSq ^ DstSq) & 0x80) + == 0
    +
      (3) 没有被塞象眼,即ucpcSquares[(SrcSq + DstSq) / 2] + == 0
    +
      ElephantEye在马的判断上用了一个诀窍:构造了一个巧妙的数组:
    +
     
    +
    const char ccHorseLegTab[512] = {
    +
     ……
    +
     0,  0,  0,  0,  0,  0, -16, +  0, -16,  0,  0,  0, +  0,  0,  0,  0,
    +
     0,  0,  0,  0,  0, -1, +  0,  0,  0,  1,  0,  0,  0,  0,  0, +  0,
    +
     0,  0,  0,  0,  0,  0,  0,  0, +  0,  0,  0,  0,  0, +  0,  0,  0,
    +
     0,  0,  0,  0,  0, -1, +  0,  0,  0,  1,  0,  0,  0,  0,  0, +  0,
    +
     0,  0,  0,  0,  0,  0, 16,  0, 16, +  0,  0,  0,  0,  0,  0,  0,
    +
     ……
    +
    };
    +
     
    +
      上面的数组中,正中心的数代表马步的起始格(红色表示),±33、±31、±18和±14是马步的增量(蓝色表示)(能被程序读到的就是这些位置),它们记录了马腿的增量(马腿的位置用绿色表示)。这样,判断合理性只需要符合两个条件:
    +
      (1) 走成马步,即(Disp = ccHorseLegTab[DstSq - SrcSq + + 256]) != 0
    +
      (2) 没有被绊马腿,即ucpcSquares[SrcSq + Disp] == 0
    +
      车和炮的判断就需要用到前面所说的位行和位列,这就需要首先要判断是否是吃子着法,然后根据不同情况作相应的判断。ElephantEye中有专门的判断着法合理性的数组,也由<pregen.cpp>生成,结构跟前面提到的着法预产生数组类似,以车在列上吃子的着法为例,只要把FileMoveRookCap[...][...][0][...][...][1]合并成位列FileMaskRookCap[...][...],判断着法合理性的时候,只要判断该位列是否包含目标格的位列(1 << (y - 3))即可。
    +
     
    +
    2.8 位棋盘
    +
     
    +
      尽管ElephantEye已经摈弃了“位棋盘”的设计,但是作为一个新兴的数据结构,尤其是“折叠位棋盘”的设计思路,还是值得一提的。“折叠位棋盘”的思想是由湖北襄樊铁路分局计算机中心的章光华提出的,首先于2004年底发表在象棋百科全书网的论坛上,该技术主要用来获得马和相()的着法。
    +
      这个思想的要点是:
    +
      (1) 棋盘必须按照列的方式对每个格子编号(如下图所示),格子的编号代表96位位棋盘中的第几位;
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    091929394959697989
    081828384858687888
    071727374757677787
    061626364656667686
    051525354555657585
    041424344454647484
    031323334353637383
    021222324252627282
    011121314151617181
    001020304050607080
    +
    + +
    +
      (2) 初始化数组一个“马腿数组”,以表示某个格子上的马可能存在的马腿,例如: +
    +
     
    +
    BitBoard HorseLegTable[90];
    +
    HorseLegTable[0] = BitMask[1] | + BitMask[10];
    +
    HorseLegTable[1] = BitMask[11] | + BitMask[20]; // BitMask[0]没必要加上去,而加上去也没错。
    +
    ……
    +
     
    +
      注意,这里仅仅是拿两个格子举例子,写在程序里的时候要用循环语句来生成马腿数组,以精简代码。
    +
      (3) 产生绊马腿的棋子的位棋盘,以红方左边未走过的马为例: +
    +
     
    +
    HorseLeg = HorseLegTable[10] & + AllPieces;
    +
     
    +
      (4) 根据马的位置和绊马腿的位棋盘,就可以知道马可以走的格子,因此可以构造这样的函数:
    +
     
    +
    BitBoard KnightPinMove(int KnightSquare, BitBoard + HorseLeg);
    +
     
    +
      为了增加运算速度,应该用查表代替运算,即把函数变成数组。那么位棋盘HorseLeg如何转化成整数呢?这就引出了“折叠位棋盘”的技术,这可以称得上是一个炫技。折叠位棋盘实际上就是位棋盘的8位校验和(CheckSum),有了折叠位棋盘后,上面的函数就可以用数组表示:
    +
     
    +
    BitBoard KnightPinMove[90][256];
    +
     
    +
      例如第10个格子上马的所有着法,用位棋盘表示为:
    +
     
    +
    KnightMoveBitBoard = KnightPinMove[10][CheckSum(HorseLegTable[10] + & AllPieces)];
    +
     
    +
      相()的着法可以采用同样的原理,首先初始化象眼数组:
    +
     
    +
    BitBoard ElephantEyeTable[90];
    +
     
    +
      随后折叠象眼的位棋盘ElephantEye,再从相()的着法预产生数组BishopPlugMove[90][256]中找到着法。
    +
    + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    135713571
    024602460
    713571357
    602460246
    571357135
    460246024
    357135713
    246024602
    135713571
    024602460
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    123456701
    012345670
    701234567
    670123456
    567012345
    456701234
    345670123
    234567012
    123456701
    012345670
    +
    按纵线编号的棋盘按横线编号的棋盘
    + +
    +
      看到这里,可能有的读者就会怀疑“折叠位棋盘”的合理性,如果折叠成4位的整数(甚至更少),把马的着法预产生数组了缩小到90x16,这显然是不合理的,为什么8位就一定合理呢?
    +
      要说明这个问题,首先来看折叠的本质——校验和(CheckSum),棋盘上的很多格子对应着校验和上固定的一位。根据这个性质,我们对棋盘重新编号,就如左图所示。假如河界下面蓝色的格子是马,那么相邻的红色格子就是马腿,4条马腿对应4个不同的编号,所以任何一种组合(一共有24 =16种组合)是不重复的。需要指出的是,这个棋盘当中所有的格子,其相邻的四个格子都有不同的编号。有趣的是,象眼同样符合这个规律(注意河界上面蓝色的格子,斜相邻的四个格子是象眼)
    +
      折叠位棋盘的唯一性是建立在“按纵线编号”的基础上的。如果按横线编号,情况就不那么幸运了,如右图所示,无论是河界下面的马,还是河界上面的象,马腿或象眼都存在编号重复的格子。
    +
      位棋盘必须按纵线编号,就是这个原因。
    +
      当然,初始化KnightPinMove[18][256]这个庞然大物,也不是一件容易的事。其实折叠位棋盘使用起来需要“折叠”,生成起来却需要“展开”(CheckSum()函数对应的Duplicate()函数,参阅<bitboard.h>)。当8位的整数展开成位棋盘后,再和某个格子的HorseLegTable作“与”运算,就可以还原为HorseLeg了。
    +
    + + +
  • 上一篇 中国象棋程序设计探索():引言
  • +
  • 下一篇 中国象棋程序设计探索():搜索和置换表
  • +
  • 返 回 象棋百科全书——电脑象棋
  • +
    +
    + + + + + + + + +

    +
    www.elephantbase.net
    +
    + + diff --git a/DOC/eleeye_utility.htm b/DOC/eleeye_utility.htm new file mode 100644 index 0000000..460fa66 --- /dev/null +++ b/DOC/eleeye_utility.htm @@ -0,0 +1,206 @@ + + + + + +中国象棋程序设计探索(十):实用程序片段 + + + + +
    +
    +
    中国象棋程序设计探索
    +
    +
     
    +
    +
    黄晨 * 20056月初稿,20075月修订
    +
    +
    ( * 上海计算机博弈研究所,eMailwebmaster@elephantbase.net)
    +
    +
     
    +
    () 实用程序片段
    +
     
    +
      ElephantEye的源程序包里有个<utility>目录,其中包含了很多实用程序片段,不仅仅是针对象棋程序的。由于程序片段中没有中文注释,所以这里作一下简要的介绍。
    +
     
    +
    10.1 汇编语言(x86asm.h)
    +
     
    +
      汇编语言是为了弥补C语言的不足,<x86asm.h>主要提供以下两方面的功能:
    +
      (1) 操控互斥(Mutex)变量的原子语句,有IncrementDecrementExchange等,用于多线程的调度;
    +
      (2) 位搜索,即调用BSF(Bit-Scan-Forward)BSR(Bit-Scan-Reverse)指令;
    +
      (3) 32位和64位整数的乘除法和位移,有LongMulDivLongMulMod等,解决了“Int64 = Int32 × Int32”等计算问题。
    +
      尽管WindowsUnix也有相应的库函数来支持这些功能,但是汇编语言通常以内联的形式调用,所以速度上会有优势。
    +
     
    +
    10.2 基本功能(base.h)
    +
     
    +
      <base.h>包括以下几个实用的函数:
    +
     
    +
      (1) 空闲函数Idle()
    +
      在程序空闲期间,应该把CPU资源交还给系统,这时就要调用空转函数Idle(),也可以直接调用WindowsUnix下都的休眠函数(Sleep()usleep())
    +
     
    +
      (2) 伪随机数LongRand()
    +
      不使用<stdlib.h>中的rand()函数,是出于以下几点考虑的:
    +
      A. rand()函数的有效位数只有15(最大数为0x7fff),这远不能满足程序的需要,用多个rand()函数来拼凑只是权益之计;
    +
      B. rand()函数的可靠性无法得到保障,因为我们不知道它的算法;
    +
      C. 如果以Zobrist键值的形式来描述开局库中的局面,那么每次给定相同的种子以后,产生的Zobrist键值都必须相同。但各种C语言的编译器中给出的rand()函数,我们无法确定其工作原理是否相同,因此也就不知道它们是否会产生相同的Zobrist键值了。
    +
     
    +
      (3) 计时函数TimerStruct::Init()TimerStruct::GetTimer()
    +
      C语言里有很多计时函数,如<time.h>中的time()ftime()clock()等,笔者认为能达到精确计时功能的只有ftime(),为此针对该函数设计了计时器,在调用时可以避开对btime结构的直接处理。计时器是类型为TimerStruct的对象,成员函数Init()把当前时间写入该计时器,而GetTimer()返回当前时间和该计时器的差值(毫秒)
    +
     
    +
    10.3 管道的行输入和行输出(pipe.h/pipe.cpp)
    +
     
    +
      ElephantEye是用“轮转”方式来接收行输入的(1.1版本开始)。轮转不需要增加线程,只需要每隔一定时间让搜索过程去处理一下接收命令的子过程就可以了。轮转的操作原理在每个操作系统下都是一样的,这种方式甚至能在不支持多任务的DOS系统下运行。在轮转中调用ReadFile()(Windows的标准库函数)read()(UNIX的标准库函数)函数时,使用起来要非常小心,通常需要调用检测输入的函数,只有确认标准输入句柄有输入时,才调用读入信息的函数。然而就检测和读取输入信息的操作而言,程序严重依赖于操作系统,有兴趣的读者可注意<pipe.cpp>中的“#ifdef _WIN32”语句,比较一下Windows代码和UNIX代码的区别。
    +
      如果输入输出的对象不是标准输入输出句柄而是程序,那么必须用管道来连接子进程的标准输入输出句柄和主进程的控制句柄。用CreatePipe()(Windows的标准库函数)pipe()(UNIX的标准库函数)建立管道时,务必要明确输入输出的方向,一个管道有两个句柄,数据总从后一个句柄输入(称为“写入端”),从前一个句柄输出(称为“读出端”),因此可以用两种方式来使用管道:
    +
      (1) 写入端定向为子进程的标准输入句柄,读出端用来向子进程发送指令;
    +
      (2) 写入端用来接收子程序的反馈信息,读出端定向为子进程的标准输出句柄。
    +
      因此,如果象棋程序的界面要控制引擎,必须建立2个管道,产生4个句柄,其中2个定向给引擎,2个用来发送和接收数据,操作非常烦琐。好在<pipe.cpp>可自动完成这一切操作,具体应用可参阅UCCI2QH(UCCI引擎的浅红象棋适配器)UCCILEAG(UCCI引擎联赛模拟器)的源程序。
    +
     
    +
    10.4 位处理(popcnt.h/popcnt.cpp)
    +
     
    +
      位搜索(BitScan)主要用于位棋盘的操作,Intel处理器提供了两个指令,即搜索最低位(BSFBitScan-Forward)和搜索最高位(BSRBitScan-Reverse)的操作,位棋盘的国际象棋程序Crafty用了FirstOne()LastOne()两个函数,就是专门调用这两个指令的。然而很多试验表明,这两个指令是非常耗时的,而“查表代替计算”会比直接用汇编指令快得多。
    +
      <bitscan.cpp>为查表计算分配了两个64K的表,直接获得16位数的位搜索结果,32位数则分解成两个16位数再分别查表,同时该模块还包含位计数(BitCount)的操作,同样用了64K的表。用<bitscan.h>提供的BitScanForward()BitScanReverse()函数做位搜索操作,耗时比直接调用汇编指令少一半,但切记使用查表函数前务必用BitScanInit()函数初始化。
    +
     
    +
    10.5 字符串分析(parse.h)
    +
     
    +
      提供了字符串比较和文件路径定位的若干实用函数。
    +
    + + +
  • 上一篇 中国象棋程序设计探索():局面评价函数
  • +
  • 下一篇
  • +
  • 返 回 象棋百科全书——电脑象棋
  • +
    +
    + + + + + + + + +

    +
    www.elephantbase.net
    +
    + + diff --git a/ELEBASE.URL b/ELEBASE.URL new file mode 100644 index 0000000..f485129 --- /dev/null +++ b/ELEBASE.URL @@ -0,0 +1,2 @@ +[InternetShortcut] +URL=http://www.elephantbase.net/ diff --git a/FEN2BMP/BBB.BMP b/FEN2BMP/BBB.BMP new file mode 100644 index 0000000..aadd35e Binary files /dev/null and b/FEN2BMP/BBB.BMP differ diff --git a/FEN2BMP/BBBM.BMP b/FEN2BMP/BBBM.BMP new file mode 100644 index 0000000..0c07d8e Binary files /dev/null and b/FEN2BMP/BBBM.BMP differ diff --git a/FEN2BMP/BBK.BMP b/FEN2BMP/BBK.BMP new file mode 100644 index 0000000..f0eafaa Binary files /dev/null and b/FEN2BMP/BBK.BMP differ diff --git a/FEN2BMP/BBKM.BMP b/FEN2BMP/BBKM.BMP new file mode 100644 index 0000000..372b56e Binary files /dev/null and b/FEN2BMP/BBKM.BMP differ diff --git a/FEN2BMP/BBN.BMP b/FEN2BMP/BBN.BMP new file mode 100644 index 0000000..23ba3f3 Binary files /dev/null and b/FEN2BMP/BBN.BMP differ diff --git a/FEN2BMP/BBNM.BMP b/FEN2BMP/BBNM.BMP new file mode 100644 index 0000000..76932c5 Binary files /dev/null and b/FEN2BMP/BBNM.BMP differ diff --git a/FEN2BMP/BBP.BMP b/FEN2BMP/BBP.BMP new file mode 100644 index 0000000..f6196a9 Binary files /dev/null and b/FEN2BMP/BBP.BMP differ diff --git a/FEN2BMP/BBPM.BMP b/FEN2BMP/BBPM.BMP new file mode 100644 index 0000000..d6eede5 Binary files /dev/null and b/FEN2BMP/BBPM.BMP differ diff --git a/FEN2BMP/BBQ.BMP b/FEN2BMP/BBQ.BMP new file mode 100644 index 0000000..5901548 Binary files /dev/null and b/FEN2BMP/BBQ.BMP differ diff --git a/FEN2BMP/BBQM.BMP b/FEN2BMP/BBQM.BMP new file mode 100644 index 0000000..598fb95 Binary files /dev/null and b/FEN2BMP/BBQM.BMP differ diff --git a/FEN2BMP/BBR.BMP b/FEN2BMP/BBR.BMP new file mode 100644 index 0000000..f44fab1 Binary files /dev/null and b/FEN2BMP/BBR.BMP differ diff --git a/FEN2BMP/BBRM.BMP b/FEN2BMP/BBRM.BMP new file mode 100644 index 0000000..79b09f3 Binary files /dev/null and b/FEN2BMP/BBRM.BMP differ diff --git a/FEN2BMP/BOARD.BMP b/FEN2BMP/BOARD.BMP new file mode 100644 index 0000000..0417c8b Binary files /dev/null and b/FEN2BMP/BOARD.BMP differ diff --git a/FEN2BMP/BOARDM.BMP b/FEN2BMP/BOARDM.BMP new file mode 100644 index 0000000..97fad2a Binary files /dev/null and b/FEN2BMP/BOARDM.BMP differ diff --git a/FEN2BMP/BWB.BMP b/FEN2BMP/BWB.BMP new file mode 100644 index 0000000..0233926 Binary files /dev/null and b/FEN2BMP/BWB.BMP differ diff --git a/FEN2BMP/BWBM.BMP b/FEN2BMP/BWBM.BMP new file mode 100644 index 0000000..a520ab0 Binary files /dev/null and b/FEN2BMP/BWBM.BMP differ diff --git a/FEN2BMP/BWK.BMP b/FEN2BMP/BWK.BMP new file mode 100644 index 0000000..55e835b Binary files /dev/null and b/FEN2BMP/BWK.BMP differ diff --git a/FEN2BMP/BWKM.BMP b/FEN2BMP/BWKM.BMP new file mode 100644 index 0000000..799b5f2 Binary files /dev/null and b/FEN2BMP/BWKM.BMP differ diff --git a/FEN2BMP/BWN.BMP b/FEN2BMP/BWN.BMP new file mode 100644 index 0000000..ec40f18 Binary files /dev/null and b/FEN2BMP/BWN.BMP differ diff --git a/FEN2BMP/BWNM.BMP b/FEN2BMP/BWNM.BMP new file mode 100644 index 0000000..9a47351 Binary files /dev/null and b/FEN2BMP/BWNM.BMP differ diff --git a/FEN2BMP/BWP.BMP b/FEN2BMP/BWP.BMP new file mode 100644 index 0000000..0012de0 Binary files /dev/null and b/FEN2BMP/BWP.BMP differ diff --git a/FEN2BMP/BWPM.BMP b/FEN2BMP/BWPM.BMP new file mode 100644 index 0000000..df9891e Binary files /dev/null and b/FEN2BMP/BWPM.BMP differ diff --git a/FEN2BMP/BWQ.BMP b/FEN2BMP/BWQ.BMP new file mode 100644 index 0000000..24b27c1 Binary files /dev/null and b/FEN2BMP/BWQ.BMP differ diff --git a/FEN2BMP/BWQM.BMP b/FEN2BMP/BWQM.BMP new file mode 100644 index 0000000..258c866 Binary files /dev/null and b/FEN2BMP/BWQM.BMP differ diff --git a/FEN2BMP/BWR.BMP b/FEN2BMP/BWR.BMP new file mode 100644 index 0000000..b8561c9 Binary files /dev/null and b/FEN2BMP/BWR.BMP differ diff --git a/FEN2BMP/BWRM.BMP b/FEN2BMP/BWRM.BMP new file mode 100644 index 0000000..b74bf3c Binary files /dev/null and b/FEN2BMP/BWRM.BMP differ diff --git a/FEN2BMP/CBA.BMP b/FEN2BMP/CBA.BMP new file mode 100644 index 0000000..957c193 Binary files /dev/null and b/FEN2BMP/CBA.BMP differ diff --git a/FEN2BMP/CBB.BMP b/FEN2BMP/CBB.BMP new file mode 100644 index 0000000..ed3983b Binary files /dev/null and b/FEN2BMP/CBB.BMP differ diff --git a/FEN2BMP/CBC.BMP b/FEN2BMP/CBC.BMP new file mode 100644 index 0000000..e0633e3 Binary files /dev/null and b/FEN2BMP/CBC.BMP differ diff --git a/FEN2BMP/CBK.BMP b/FEN2BMP/CBK.BMP new file mode 100644 index 0000000..d3952c9 Binary files /dev/null and b/FEN2BMP/CBK.BMP differ diff --git a/FEN2BMP/CBN.BMP b/FEN2BMP/CBN.BMP new file mode 100644 index 0000000..84ec36c Binary files /dev/null and b/FEN2BMP/CBN.BMP differ diff --git a/FEN2BMP/CBOARD.BMP b/FEN2BMP/CBOARD.BMP new file mode 100644 index 0000000..fc1b4c1 Binary files /dev/null and b/FEN2BMP/CBOARD.BMP differ diff --git a/FEN2BMP/CBP.BMP b/FEN2BMP/CBP.BMP new file mode 100644 index 0000000..2f646e5 Binary files /dev/null and b/FEN2BMP/CBP.BMP differ diff --git a/FEN2BMP/CBR.BMP b/FEN2BMP/CBR.BMP new file mode 100644 index 0000000..b5cc44a Binary files /dev/null and b/FEN2BMP/CBR.BMP differ diff --git a/FEN2BMP/CCHESS.FEN b/FEN2BMP/CCHESS.FEN new file mode 100644 index 0000000..207ad85 --- /dev/null +++ b/FEN2BMP/CCHESS.FEN @@ -0,0 +1 @@ +rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w - - 0 1 \ No newline at end of file diff --git a/FEN2BMP/CHESS.FEN b/FEN2BMP/CHESS.FEN new file mode 100644 index 0000000..df40e92 --- /dev/null +++ b/FEN2BMP/CHESS.FEN @@ -0,0 +1 @@ +rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 \ No newline at end of file diff --git a/FEN2BMP/CRA.BMP b/FEN2BMP/CRA.BMP new file mode 100644 index 0000000..6013a9f Binary files /dev/null and b/FEN2BMP/CRA.BMP differ diff --git a/FEN2BMP/CRAM.BMP b/FEN2BMP/CRAM.BMP new file mode 100644 index 0000000..4df7dc5 Binary files /dev/null and b/FEN2BMP/CRAM.BMP differ diff --git a/FEN2BMP/CRB.BMP b/FEN2BMP/CRB.BMP new file mode 100644 index 0000000..816c16d Binary files /dev/null and b/FEN2BMP/CRB.BMP differ diff --git a/FEN2BMP/CRBM.BMP b/FEN2BMP/CRBM.BMP new file mode 100644 index 0000000..99ee50c Binary files /dev/null and b/FEN2BMP/CRBM.BMP differ diff --git a/FEN2BMP/CRC.BMP b/FEN2BMP/CRC.BMP new file mode 100644 index 0000000..38586d9 Binary files /dev/null and b/FEN2BMP/CRC.BMP differ diff --git a/FEN2BMP/CRCM.BMP b/FEN2BMP/CRCM.BMP new file mode 100644 index 0000000..866c54a Binary files /dev/null and b/FEN2BMP/CRCM.BMP differ diff --git a/FEN2BMP/CRK.BMP b/FEN2BMP/CRK.BMP new file mode 100644 index 0000000..5e8d0b9 Binary files /dev/null and b/FEN2BMP/CRK.BMP differ diff --git a/FEN2BMP/CRKM.BMP b/FEN2BMP/CRKM.BMP new file mode 100644 index 0000000..f548242 Binary files /dev/null and b/FEN2BMP/CRKM.BMP differ diff --git a/FEN2BMP/CRN.BMP b/FEN2BMP/CRN.BMP new file mode 100644 index 0000000..e1a4def Binary files /dev/null and b/FEN2BMP/CRN.BMP differ diff --git a/FEN2BMP/CRNM.BMP b/FEN2BMP/CRNM.BMP new file mode 100644 index 0000000..899af99 Binary files /dev/null and b/FEN2BMP/CRNM.BMP differ diff --git a/FEN2BMP/CRP.BMP b/FEN2BMP/CRP.BMP new file mode 100644 index 0000000..d014077 Binary files /dev/null and b/FEN2BMP/CRP.BMP differ diff --git a/FEN2BMP/CRPM.BMP b/FEN2BMP/CRPM.BMP new file mode 100644 index 0000000..3e78b04 Binary files /dev/null and b/FEN2BMP/CRPM.BMP differ diff --git a/FEN2BMP/CRR.BMP b/FEN2BMP/CRR.BMP new file mode 100644 index 0000000..44effeb Binary files /dev/null and b/FEN2BMP/CRR.BMP differ diff --git a/FEN2BMP/CRRM.BMP b/FEN2BMP/CRRM.BMP new file mode 100644 index 0000000..889322b Binary files /dev/null and b/FEN2BMP/CRRM.BMP differ diff --git a/FEN2BMP/FEN2BMP.C b/FEN2BMP/FEN2BMP.C new file mode 100644 index 0000000..3248229 --- /dev/null +++ b/FEN2BMP/FEN2BMP.C @@ -0,0 +1,203 @@ +/* +FEN->BMP Convertor - a Bitmap Generation Program for Chess and Chinese Chess +Designed by Morning Yellow, Version: 1.1, Last Modified: Feb. 2006 +Copyright (C) 2004-2006 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +static int FenPiece(int nArg) { + switch (nArg) { + case 'K': + return 1; + case 'Q': + case 'A': + return 2; + case 'B': + case 'E': + return 3; + case 'N': + case 'H': + return 4; + case 'R': + return 5; + case 'C': + return 6; + case 'P': + return 7; + default: + return 0; + } +} + +static const char *cszWWPiece[8] = {NULL, "WWK.BMP", "WWQ.BMP", "WWB.BMP", "WWN.BMP", "WWR.BMP", "WWP.BMP", "WWP.BMP"}; +static const char *cszWBPiece[8] = {NULL, "WBK.BMP", "WBQ.BMP", "WBB.BMP", "WBN.BMP", "WBR.BMP", "WBP.BMP", "WBP.BMP"}; +static const char *cszBWPiece[8] = {NULL, "BWK.BMP", "BWQ.BMP", "BWB.BMP", "BWN.BMP", "BWR.BMP", "BWP.BMP", "BWP.BMP"}; +static const char *cszBBPiece[8] = {NULL, "BBK.BMP", "BBQ.BMP", "BBB.BMP", "BBN.BMP", "BBR.BMP", "BBP.BMP", "BBP.BMP"}; +static const char *cszBWPieceMono[8] = {NULL, "BWKM.BMP", "BWQM.BMP", "BWBM.BMP", "BWNM.BMP", "BWRM.BMP", "BWPM.BMP", "BWPM.BMP"}; +static const char *cszBBPieceMono[8] = {NULL, "BBKM.BMP", "BBQM.BMP", "BBBM.BMP", "BBNM.BMP", "BBRM.BMP", "BBPM.BMP", "BBPM.BMP"}; +static const char *cszCRPiece[8] = {NULL, "CRK.BMP", "CRA.BMP", "CRB.BMP", "CRN.BMP", "CRR.BMP", "CRC.BMP", "CRP.BMP"}; +static const char *cszCRPieceMono[8] = {NULL, "CRKM.BMP", "CRAM.BMP", "CRBM.BMP", "CRNM.BMP", "CRRM.BMP", "CRCM.BMP", "CRPM.BMP"}; +static const char *cszCBPiece[8] = {NULL, "CBK.BMP", "CBA.BMP", "CBB.BMP", "CBN.BMP", "CBR.BMP", "CBC.BMP", "CBP.BMP"}; + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { + int i, j, k; + BOOL bMono; + char *lpChar, *lpBmpFileName; + int nBoard[10][9]; + char szBmpFileName[1024]; + char szBuffer[1024]; + DWORD dwBytesAccessed; + HANDLE hFenFile, hOutFile, hBoardFile, hPieceFile; + // Init Proc: + if (lpCmdLine == NULL || lpCmdLine[0] == '\0') { + MessageBox(NULL, "=== FEN->BMP Convertor ===\nUsage: FEN2BMP FEN-File", NULL, MB_ICONEXCLAMATION); + return 0; + } + strcpy(szBuffer, lpCmdLine + (lpCmdLine[0] == '\"' ? 1 : 0)); + lpChar = strchr(szBuffer, '\"'); + if (lpChar != NULL) { + *lpChar = '\0'; + } + hFenFile = CreateFile(szBuffer, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFenFile == INVALID_HANDLE_VALUE) { + MessageBox(NULL, szBuffer, NULL, MB_ICONEXCLAMATION); + strcpy(szBuffer, "Unable to Open File: "); + strcat(szBuffer, lpCmdLine); + MessageBox(NULL, szBuffer, NULL, MB_ICONEXCLAMATION); + return 0; + } + lpChar = GetCommandLine(); + i = lpCmdLine - lpChar - strlen("FEN2BMP.EXE\" ") - 1; + strncpy(szBmpFileName, lpChar + 1, i); + lpBmpFileName = szBmpFileName + i; + // Read Proc: + ReadFile(hFenFile, szBuffer, 1023, &dwBytesAccessed, NULL); + szBuffer[dwBytesAccessed] = '\0'; + for (i = 0; i < 10; i ++) { + for (j = 0; j < 9; j ++) { + nBoard[i][j] = 0; + } + } + lpChar = szBuffer; + i = 0; + j = 0; + while (*lpChar > ' ') { + if (*lpChar == '/') { + i ++; + j = 0; + if (i >= 10) { + break; + } + } else if (*lpChar >= '1' && *lpChar <= '9') { + for (k = '0'; k < *lpChar; k ++) { + if (j >= 9) { + break; + } + j ++; + } + } else if (*lpChar >= 'A' && *lpChar <= 'Z') { + if (j < 9) { + nBoard[i][j] = FenPiece(*lpChar); + } + j ++; + } else if (*lpChar >= 'a' && *lpChar <= 'z') { + if (j < 9) { + nBoard[i][j] = -FenPiece(*lpChar - 'a' + 'A'); + } + j ++; + } + lpChar ++; + } + CloseHandle(hFenFile); + // Write Proc: + bMono = FALSE; + if (MessageBox(NULL, "Draw Board for Monochromatic Publications?", "Choose Board Mode", MB_YESNO | MB_ICONQUESTION) == IDYES) { + bMono = TRUE; + } + if (i == 7) { + strcpy(lpBmpFileName, "FEN2BMP.BMP"); + hOutFile = CreateFile(szBmpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + strcpy(lpBmpFileName, bMono ? "BOARDM.BMP" : "BOARD.BMP"); + hBoardFile = CreateFile(szBmpFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + ReadFile(hBoardFile, szBuffer, 118, &dwBytesAccessed, NULL); + WriteFile(hOutFile, szBuffer, 118, &dwBytesAccessed, NULL); // Write Header; + for (i = 0; i < 32; i ++) { + ReadFile(hBoardFile, szBuffer, 1024, &dwBytesAccessed, NULL); + WriteFile(hOutFile, szBuffer, 1024, &dwBytesAccessed, NULL); // Write Board; + } + CloseHandle(hBoardFile); + for (i = 0; i < 8; i ++) { + for (j = 0; j < 8; j ++) { + k = nBoard[i][j]; + if (k != 0) { + if ((i + j) % 2 == 0) { // Black Square + strcpy(lpBmpFileName, k > 0 ? cszWWPiece[k] : cszWBPiece[-k]); + } else { // White Square + if (bMono) { + strcpy(lpBmpFileName, k > 0 ? cszBWPieceMono[k] : cszBBPieceMono[-k]); + } else { + strcpy(lpBmpFileName, k > 0 ? cszBWPiece[k] : cszBBPiece[-k]); + } + } + hPieceFile = CreateFile(szBmpFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + SetFilePointer(hPieceFile, 118, NULL, FILE_BEGIN); + for (k = 0; k < 32; k ++) { + ReadFile(hPieceFile, szBuffer, 16, &dwBytesAccessed, NULL); + SetFilePointer(hOutFile, (7 - i) * 4096 + k * 128 + j * 16 + 118, NULL, FILE_BEGIN); + WriteFile(hOutFile, szBuffer, 16, &dwBytesAccessed, NULL); + } + CloseHandle(hPieceFile); + } + } + } + CloseHandle(hOutFile); + } else if (i == 9) { + strcpy(lpBmpFileName, "FEN2BMP.BMP"); + hOutFile = CreateFile(szBmpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + strcpy(lpBmpFileName, "CBOARD.BMP"); + hBoardFile = CreateFile(szBmpFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + ReadFile(hBoardFile, szBuffer, 118, &dwBytesAccessed, NULL); + WriteFile(hOutFile, szBuffer, 118, &dwBytesAccessed, NULL); // Write Header; + for (i = 0; i < 36; i ++) { + ReadFile(hBoardFile, szBuffer, 702, &dwBytesAccessed, NULL); + WriteFile(hOutFile, szBuffer, 702, &dwBytesAccessed, NULL); // Write Board; + } + CloseHandle(hBoardFile); + for (i = 0; i < 10; i ++) { + for (j = 0; j < 9; j ++) { + k = nBoard[i][j]; + if (k != 0) { + strcpy(lpBmpFileName, k > 0 ? (bMono ? cszCRPieceMono[k] : cszCRPiece[k]) : cszCBPiece[-k]); + hPieceFile = CreateFile(szBmpFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + SetFilePointer(hPieceFile, 118, NULL, FILE_BEGIN); + for (k = 0; k < 18; k ++) { + ReadFile(hPieceFile, szBuffer, 12, &dwBytesAccessed, NULL); + SetFilePointer(hOutFile, (9 - i) * 2592 + k * 108 + j * 12 + 118, NULL, FILE_BEGIN); + WriteFile(hOutFile, szBuffer, 9, &dwBytesAccessed, NULL); + } + CloseHandle(hPieceFile); + } + } + } + CloseHandle(hOutFile); + } + strcpy(lpBmpFileName, "FEN2BMP.BMP"); + ShellExecute(NULL, NULL, szBmpFileName, NULL, NULL, SW_SHOW); + return 0; +} diff --git a/FEN2BMP/FEN2BMP.ISS b/FEN2BMP/FEN2BMP.ISS new file mode 100644 index 0000000..d7dca5f --- /dev/null +++ b/FEN2BMP/FEN2BMP.ISS @@ -0,0 +1,89 @@ +; Script generated by the Inno Setup Script Wizard. +; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! + +[Setup] +AppName=FEN2BMP +AppVerName=FEN2BMP +AppPublisher=www.elephantbase.net +AppPublisherURL=http://www.elephantbase.net/ +AppSupportURL=http://www.elephantbase.net/ +AppUpdatesURL=http://www.elephantbase.net/ +DefaultDirName={pf}\FEN2BMP +DefaultGroupName=FEN2BMP +OutputBaseFilename=setup +OutputDir=. +Compression=lzma +SolidCompression=yes + +[Files] +Source: "FEN2BMP.EXE"; DestDir: "{app}"; Flags: ignoreversion +Source: "README.TXT"; DestDir: "{app}"; Flags: ignoreversion +Source: "CHESS.FEN"; DestDir: "{app}"; Flags: ignoreversion +Source: "CCHESS.FEN"; DestDir: "{app}"; Flags: ignoreversion +Source: "BOARD.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BOARD_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CBOARD.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "WWK.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "WWQ.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "WWB.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "WWN.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "WWR.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "WWP.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "WBK.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "WBQ.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "WBB.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "WBN.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "WBR.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "WBP.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BWK.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BWQ.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BWB.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BWN.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BWR.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BWP.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BBK.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BBQ.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BBB.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BBN.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BBR.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BBP.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BWK_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BWQ_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BWB_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BWN_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BWR_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BWP_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BBK_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BBQ_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BBB_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BBN_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BBR_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "BBP_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CRK.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CRA.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CRB.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CRN.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CRR.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CRC.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CRP.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CRK_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CRA_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CRB_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CRN_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CRR_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CRC_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CRP_.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CBK.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CBA.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CBB.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CBN.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CBR.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CBC.BMP"; DestDir: "{app}"; Flags: ignoreversion +Source: "CBP.BMP"; DestDir: "{app}"; Flags: ignoreversion +; NOTE: Don't use "Flags: ignoreversion" on any shared system files + +[Registry] +Root: HKCR; Subkey: "WinBoard.FEN\Shell\Shell\FEN2BMP"; ValueType: string; ValueData: "&View with FEN2BMP" +Root: HKCR; Subkey: "WinBoard.FEN\Shell\Shell\FEN2BMP\command"; ValueType: string; ValueData: "{app}\FEN2BMP.EXE %1" +Root: HKCR; Subkey: "ElephantBoard.FEN\Shell\FEN2BMP"; ValueType: string; ValueData: "&View with FEN2BMP" +Root: HKCR; Subkey: "ElephantBoard.FEN\Shell\FEN2BMP\command"; ValueType: string; ValueData: "{app}\FEN2BMP.EXE %1" diff --git a/FEN2BMP/README.TXT b/FEN2BMP/README.TXT new file mode 100644 index 0000000..4567fd5 --- /dev/null +++ b/FEN2BMP/README.TXT @@ -0,0 +1,28 @@ +FEN2BMP——国际象棋和中国象棋通用的棋盘图片生成器 + +  想把象棋棋盘做成GIF图片挂到你的网页上去吗?FEN2BMP 可以为你解决这个问题。 +  FEN2BMP 可以把棋盘文件FEN转换为位图文件BMP,它的便利之处在于: +  (1) 可以自动区别国际象棋的FEN文件和中国象棋的FEN文件; +  (2) 可以自己定义棋盘图片的颜色和棋子的图案; +  (3) 转换成BMP文件后会自动打开。 + +  下面来看一下如何用 WinBoard 和 FEN2BMP 产生国际象棋棋盘的图片。 +  (1) 首先确认你的电脑是安装好 WinBoard 和 FEN2BMP 的,当 FEN2BMP 安装完毕后,用鼠标右键去点FEN文件时,菜单中多出一项“View with FEN2BMP”; +  (2) 打开 WinBoard,把棋盘摆到某个特定的局面; +  (3) 用 File 菜单里的 Save Position 命令,把局面保存为 FEN 文件(最好保存在 Windows 的桌面上,这样操作起来方便),随便起什么名字; +  (4) 用鼠标右键去点刚才保存的FEN文件,然后选择“View with FEN2BMP”; +  (5) 程序会提示是否要为黑白报刊产生图片,如果你想得到醒目的彩色图片,请选择否; +  (6) 程序立刻就产生了 FEN2BMP.BMP,并且按默认方式打开(通常是“画图”或 ACDSee); +  (7) 用“画图”或 ACDSee 把它转化为GIF文件,这样就可以挂到你的网页上去了; +  (8) 当你需要黑白图片时,只要用“画图”程序把它转换成黑白的就可以了(到“属性”对话框里去找找看)。 + +  如果你对 FEN2BMP 产生的图片不满意,那么你可以修改它所附带的BMP文件,可以是棋盘也可以是棋子,但是要注意: +  (1) 不能改动图片的色彩数目,所有的图片都必须是16色的,且使用 Windows 默认的调色板; +  (2) 不能改动图片的尺寸,所以文件的大小总是严格固定,如果你要新做一个位图文件,那么做完后必须先和原来的文件比较一下字节数,字节数一样才能替换原来的文件; +  (3) 如果你觉得以上两条限制无法让你设计出满意的棋盘,那么请和FEN2BMP的作者联系:morning_yellow@elephantbase.net。 + +  好了,希望 FEN2BMP 能给你带来方便。 +  FEN2BMP 的下载地址:http://www.elephantbase.net/download/fen2bmp.exe + +  黄晨,2005年3月 +  象棋百科全书 http://www.elephantbase.net/ 期待您的关注。 \ No newline at end of file diff --git a/FEN2BMP/WBB.BMP b/FEN2BMP/WBB.BMP new file mode 100644 index 0000000..9899196 Binary files /dev/null and b/FEN2BMP/WBB.BMP differ diff --git a/FEN2BMP/WBK.BMP b/FEN2BMP/WBK.BMP new file mode 100644 index 0000000..e146252 Binary files /dev/null and b/FEN2BMP/WBK.BMP differ diff --git a/FEN2BMP/WBN.BMP b/FEN2BMP/WBN.BMP new file mode 100644 index 0000000..2522782 Binary files /dev/null and b/FEN2BMP/WBN.BMP differ diff --git a/FEN2BMP/WBP.BMP b/FEN2BMP/WBP.BMP new file mode 100644 index 0000000..b854984 Binary files /dev/null and b/FEN2BMP/WBP.BMP differ diff --git a/FEN2BMP/WBQ.BMP b/FEN2BMP/WBQ.BMP new file mode 100644 index 0000000..f2457e5 Binary files /dev/null and b/FEN2BMP/WBQ.BMP differ diff --git a/FEN2BMP/WBR.BMP b/FEN2BMP/WBR.BMP new file mode 100644 index 0000000..917af74 Binary files /dev/null and b/FEN2BMP/WBR.BMP differ diff --git a/FEN2BMP/WWB.BMP b/FEN2BMP/WWB.BMP new file mode 100644 index 0000000..f5c99f6 Binary files /dev/null and b/FEN2BMP/WWB.BMP differ diff --git a/FEN2BMP/WWK.BMP b/FEN2BMP/WWK.BMP new file mode 100644 index 0000000..97ee063 Binary files /dev/null and b/FEN2BMP/WWK.BMP differ diff --git a/FEN2BMP/WWN.BMP b/FEN2BMP/WWN.BMP new file mode 100644 index 0000000..005eb01 Binary files /dev/null and b/FEN2BMP/WWN.BMP differ diff --git a/FEN2BMP/WWP.BMP b/FEN2BMP/WWP.BMP new file mode 100644 index 0000000..597242e Binary files /dev/null and b/FEN2BMP/WWP.BMP differ diff --git a/FEN2BMP/WWQ.BMP b/FEN2BMP/WWQ.BMP new file mode 100644 index 0000000..0670047 Binary files /dev/null and b/FEN2BMP/WWQ.BMP differ diff --git a/FEN2BMP/WWR.BMP b/FEN2BMP/WWR.BMP new file mode 100644 index 0000000..13beeea Binary files /dev/null and b/FEN2BMP/WWR.BMP differ diff --git a/LEAGUE/LIVE/background.gif b/LEAGUE/LIVE/background.gif new file mode 100644 index 0000000..b981179 Binary files /dev/null and b/LEAGUE/LIVE/background.gif differ diff --git a/LEAGUE/LIVE/counter.php b/LEAGUE/LIVE/counter.php new file mode 100644 index 0000000..f97e9fa --- /dev/null +++ b/LEAGUE/LIVE/counter.php @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/LEAGUE/LIVE/counter_conf.php b/LEAGUE/LIVE/counter_conf.php new file mode 100644 index 0000000..b686099 --- /dev/null +++ b/LEAGUE/LIVE/counter_conf.php @@ -0,0 +1,8 @@ + diff --git a/LEAGUE/LIVE/elephantbase.gif b/LEAGUE/LIVE/elephantbase.gif new file mode 100644 index 0000000..858c93e Binary files /dev/null and b/LEAGUE/LIVE/elephantbase.gif differ diff --git a/LEAGUE/LIVE/footer.htm b/LEAGUE/LIVE/footer.htm new file mode 100644 index 0000000..04fb64d --- /dev/null +++ b/LEAGUE/LIVE/footer.htm @@ -0,0 +1,25 @@ + + + + + + + + + + + +
     
     
    \ No newline at end of file diff --git a/LEAGUE/LIVE/header.htm b/LEAGUE/LIVE/header.htm new file mode 100644 index 0000000..ea40ead --- /dev/null +++ b/LEAGUE/LIVE/header.htm @@ -0,0 +1,24 @@ + + + + + + + + + + + +
     
     
    \ No newline at end of file diff --git a/LEAGUE/LIVE/pgn.gif b/LEAGUE/LIVE/pgn.gif new file mode 100644 index 0000000..d10de8c Binary files /dev/null and b/LEAGUE/LIVE/pgn.gif differ diff --git a/LEAGUE/LIVE/upload.htm b/LEAGUE/LIVE/upload.htm new file mode 100644 index 0000000..5bb48bf --- /dev/null +++ b/LEAGUE/LIVE/upload.htm @@ -0,0 +1,21 @@ + +
    + + + + + + + + + + + + + + + + +
    Local File:
    Remote File:
    Password:
    +
    + \ No newline at end of file diff --git a/LEAGUE/LIVE/upload.php b/LEAGUE/LIVE/upload.php new file mode 100644 index 0000000..5e94416 --- /dev/null +++ b/LEAGUE/LIVE/upload.php @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/LEAGUE/LIVE/upload_conf.php b/LEAGUE/LIVE/upload_conf.php new file mode 100644 index 0000000..94b851e --- /dev/null +++ b/LEAGUE/LIVE/upload_conf.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/LEAGUE/MAKEFILE.BAT b/LEAGUE/MAKEFILE.BAT new file mode 100644 index 0000000..21deee4 --- /dev/null +++ b/LEAGUE/MAKEFILE.BAT @@ -0,0 +1,5 @@ +@ECHO OFF +RC ..\RES\UCCILEAG.RC +CL /DNDEBUG /O2 /W3 /Fe..\BIN\UCCILEAG.EXE ..\BASE\PIPE.CPP ..\BASE\WSOCKBAS.CPP ..\CODEC\BASE64\BASE64.CPP ..\ELEEYE\PREGEN.CPP ..\ELEEYE\POSITION.CPP ..\ELEEYE\GENMOVES.CPP ..\CCHESS\CCHESS.CPP ..\CCHESS\PGNFILE.CPP UCCILEAG.CPP ..\RES\UCCILEAG.RES SHLWAPI.LIB WSOCK32.LIB +DEL ..\RES\UCCILEAG.RES +DEL *.OBJ diff --git a/LEAGUE/README.TXT b/LEAGUE/README.TXT new file mode 100644 index 0000000..d9ed497 --- /dev/null +++ b/LEAGUE/README.TXT @@ -0,0 +1,106 @@ +UCCI引擎联赛模拟器 (版本:3.7) + +一、概述 + +  “UCCI引擎联赛模拟器”为UCCI引擎测试和比赛提供了自动批量对局的平台,并支持网上直播。该程序以Windows和UNIX操作系统为平台,可在命令行模式下完成下以几个功能: +  (1) 生成联赛的循环赛对阵表; +  (2) 对于每局比赛,负责启动引擎、控制时间、控制走棋、生成棋谱、处理结果等; +  (3) 计算各队的胜负情况和等级分,生成联赛排名表; +  (4) 记录联赛进程,当程序意外中止时,重新启动程序可让联赛紧接着上次记录的进程继续进行; +  (5) 如果模拟器运行在多处理器的计算机上,可用多个处理器同时举行多场比赛; +  (6) 通过Web服务器对联赛进行网上直播。 + +二、标准CPU时间 + +  为让UCCI引擎适应各种时限的赛制,并让比赛具有客观公正性,比赛的时限应根据处理器的速度作相应的调整。例如,快棋赛的包干时间是600秒(10分钟),通常会在3000MHz的处理器上举行这样的比赛,以测试象棋引擎的水平。如果象棋引擎运行在1000MHz的处理器上,那么测试同一等级的快棋赛,就必须把时限调整为1800秒。 +  这样,就有必要规定一个处理器的标准速度(如3000MHz),一段程序的运算量就可以用“标准CPU时间”来衡量。如果象棋引擎运行在不同速度的处理器上(如1000MHz),就必须根据该处理器的速度,把时限由标准CPU时间(如600秒)调整为该处理器的实际时间(900秒)。 +  一般来说,象棋引擎都只使用处理器的整数运算功能,因此建议以“筛选素数”程序作为标准测试程序,来测定不同处理器的速度。UCCI引擎联赛模拟器附带的测试程序(GENPRIME.EXE)以简单筛法求得2^30以内的素数个数,程序作者建议把该程序的运行时间定为30秒的标准CPU时间,这样大约相当于3000MHz的x86型处理器的速度。 + +三、配置文件 + +  UCCI引擎联赛模拟器的程序名称是 UCCILEAG.EXE ,执行前需要事先编辑配置文件 UCCILEAG.INI ,说明联赛赛制信息、UCCI引擎的参赛信息和网上直播配置,模拟器运行时则会显示联赛进程和结果。 + +  配置文件由[League]节、[Teams]节和[Live]节构成,其中[League]节规定了联赛的赛事信息,各个属性的意义是: +  (1) Event:赛事名称(可以用中文); +  (2) Site:比赛地点(可以用中文); +  (3) Processors:使用的处理器数,最多允许使用32个处理器(默认使用1个处理器); +  (4) Roundrobins:主客场(先后手)双循环次数,最多允许进行10次双循环(默认进行1次双循环); +  (5) InitialTime:加时制的初始时限,单位是分钟,最长时限可达500分钟; +  (6) IncrementalTime:加时制的每步增加时间,单位是秒,设成零即为包干制,最长加时可达500秒; +  (7) StoppingTime:模拟器向引擎发送"stop"指令(出现超时情况)后等待引擎反馈的时间,超出该时间则视为超时,单位是毫秒,最长可等待500毫秒; +  (8) StandardCpuTime:和一秒钟标准CPU时间等同的实际时间,单位是毫秒,默认值是1000,值越大说明机器速度越慢,最长可设成5000; +  (9) Promotion:是否允许仕(士)相(象)升变成兵(卒),这是一种中国象棋的改良玩法,其值是On或Off,默认值是Off(即默认采用常规走法)。 + +  [Teams]节规定了参赛队的信息,每个参赛队占据一行,其格式是: + +    Team=<缩写>,<引擎名称>,<等级分>,,<引擎文件>,,<引擎介绍URL(可选)> + +  其中是文本文件,在棋局开始前(联赛模拟器向UCCI引擎发送 ucci 指令后),需要额外给予引擎的一些初始化指令(例如 setoption loadbook)可以加到这个配置文件中。同一个UCCI引擎可以代表几个不同的参赛队,使用不同的UCCI选项配置文件,测试不同选项对引擎战绩的影响。 + +  [Live]节规定了网上直播配置,各个属性的意义是: +  (1) Host:直播主机,可以是IP地址、域名以及虚拟主机的域名; +  (2) Path:上传文件的程序路径; +  (3) Password:上传文件的口令; +  (4) Extension:上传文件的后缀,通常是html、htm、asp、php等被Web服务器自动解析为 text/html 类型的文件; +  (5) Counter:计数器的路径,如果指定计数器,则直播主页面会显示“您是第<计数器>位观众”的信息; +  (6) Header:直播页面页眉文件,直播时会将此文件(本地文件,含有HTML内容)插入到直播页面的页眉位置; +  (7) Footer:直播页面页脚文件,直播时会将此文件(本地文件,含有HTML内容)插入到直播页面的页脚位置; +  (8) Port:HTTP端口,默认是80; +  (9) Refresh:直播棋局页面的自动刷新时间,单位是秒; +  (10) Interval:两次上传文件的间隔时间,单位是毫秒,设置这个属性可以避免某些Web服务器因为客户频繁提交而拒绝响应。 + +  如果进行联赛的主机无法直接访问Web服务器,那么可以在[Proxy]节中配置HTTP代理的参数,各个属性的意义是: +  (1) ProxyHost:HTTP代理服务器的地址; +  (2) ProxyPort:HTTP代理服务端口; +  (3) ProxyUser:HTTP代理的认证用户名; +  (4) ProxyPassword:HTTP代理的认证口令。 +  模拟器只通过POST方法访问HTTP代理(不使用CONNECT方法),所以代理服务必须支持POST方法。 + +  需要注意的问题有: +  (1) 每条配置信息必须按照<属性>=<值>的格式录入,等号两边都不能有空格。建议用户在样板配置文件上作修改,以免造成标签的拼写错误。 +  (2) 如果操作系统语言设定正确,那么配置文件中的赛事名称、比赛地点、引擎名称等可显示的信息可以用中文。 +  (3) 可以把参赛的引擎放在不同的目录下,在<引擎文件>和<配置文件>两项上加上完整的路径,Windows下的路径用“\”分隔,UNIX下的路径用“/”分隔。 + +四、直播服务器 + +  UCCI引擎联赛模拟器的网上直播功能,适合多种类型的Web服务器,独立的Web服务器、虚拟主机、Web虚拟目录都可以为UCCI引擎联赛提供直播服务。 +  Web服务器必须支持POST命令上传文件,模拟器会通过HTTP的POST命令把联赛首页、棋局页面和棋谱文件即时上传到服务器上,实现直播功能。模拟器为支持PHP的Web服务器提供了接收上传文件的脚本,即 upload.php ,联赛网上直播时需要在配置文件中设定该脚本的路径(即[Live]节的Path项)。如果Web服务器只支持ASP、Java/JSP等特定的脚本语言,则联赛主办者应该仿照 upload.php 编写脚本。 +  上传文件通常设有口令保护,口令被写入上传脚本中,并在模拟器的配置文件中设置相同的口令。口令必须妥善保管,否则可能被其他人上传恶意脚本,从而破坏服务器。由于观众不会直接访问到联赛运行的计算机,也不会直接获取到服务器上的上传脚本,所以口令在直播过程中是相对安全的。目前模拟器不支持SSL,如果需要加强口令保护,可以在直播计算机上设置SSL客户端代理,在服务器上启用HTTPS服务。 +  模拟器还为直播提供了页面背景、图标以及访问计数器等附件,建议直播前连同上传脚本一起事先把这些附件上传到服务器上。 + +五、棋谱文件、日志文件和进程文件 + +  对每一场比赛(每一局棋),模拟器都会产生一个棋谱文件,名称是 XXX-YYYn.PGN ,XXX是主队(红方)的缩写,YYY是客队(黑方)的缩写,n代表第(n + 1)个双循环次数。如果联赛是在线直播的,那么棋谱文件会上传到服务器上,并可供观众下载。 +  日志文件是 XXX-YYYn.LOG ,记录模拟器和两个引擎的通讯信息。 +  进程文件是 XXX-YYYn.CHK ,记录每个引擎反馈的着法和走完该着所剩的时间。当模拟器意外终止并重新启动后,就会由进程文件中的记录得到引擎的着法,重新生成输出报告和棋谱文件,从而节省了引擎重复运算的时间。 + +六、对引擎的建议 + +  为提高测试和比赛的效率,建议引擎支持以下UCCI指令和反馈信息: + +  一、建议引擎支持“双时限制”,在时间策略上同时考虑对方的用时情况。 +  模拟器会以下面的形式告诉引擎“走棋方的时限”和“对方的时限”: +    go time <走棋方的时间> increment <加时> opptime <对方的时间> oppincrement <加时> +  注意: +  (1) <对方的时间>指的是对方上一步走完以后,剩余时间加上“加时”以后的时间; +  (2) 如果引擎不支持双时限制,模拟器也会按照上面的格式发送走棋指令,引擎只要不理会 opptime 和 oppincrement 便可。 + +  二、建议引擎支持“提和”与“认输”功能,这有助于缩短比赛时间。 +  (1) 引擎提和与接受提和的指令是:bestmove <着法> [ponder <后台思考>] draw; +  (2) 引擎认输的指令是:bestmove <着法> [ponder <后台思考>] resign。 +  注意: +  (1) 模拟器可能会向引擎发送 go draw time ... 的指令,说明对方已提和,即便引擎不支持提和与接受提和,也不能因为有了 draw 关键字而解析不出 go 指令; +  (2) 双方连续给出有 draw 的反馈,模拟器就会终止比赛,认定和棋。 + +  三、建议引擎支持“毫秒制”,以提高时间策略的精确度。 +  (1) 模拟器支持“秒制”和“毫秒制”,如果引擎在启动时没有输出反馈 option usemillisec ..., 则采用“秒制”。 +  (2) 支持“毫秒制”的引擎必须在启动时输出反馈 option usemillisec ...,模拟器在接收到这条反馈时,会向引擎发送指令 setoption usemillisec true,以后每条指令的时间单位都是“毫秒”。 + +  四、建议引擎支持禁手指令,以避免因规则解释不当而被判负。 +  当重复局面即将达到3次时,模拟器将会向引擎发送 banmoves ... 指令,告诉引擎走哪步棋是长打判负的。引擎反馈着法时,不能走出 banmoves 指令中所规定的着法,否则就可能在后续的着法中被判长打作负。 +   + +七、程序下载 + +  UCCI引擎联赛模拟器通常会跟UCCI引擎ElephantEye一同发布,源程序及其Windows版本下的编译程序下载地址是: +    http://www.elephantbase.net/download/eleeye.7z。 diff --git a/LEAGUE/RULE.TXT b/LEAGUE/RULE.TXT new file mode 100644 index 0000000..de20bf8 --- /dev/null +++ b/LEAGUE/RULE.TXT @@ -0,0 +1,59 @@ +电脑象棋联赛规则(草案) + +黄晨 * 2005年12月初稿,2007年9月修订 +( * 上海计算机博弈研究所,eMail:webmaster@elephantbase.net) + +一、总则 + +  1.1 为促进中国象棋电脑程序的发展,www.elephantbase.net 网站将定期举办UCCI引擎联赛,建立UCCI引擎排名系统。 +  1.2 参赛程序可以是商业的或免费的,可以是发布的或未发布的,未发布的程序如果采用公开源程序的代码,也必须遵循源程序的代码使用协议(如GPL协议)。 +  1.3 参赛程序的编译后版本必须符合以下条件:(1) 遵循UCCI引擎协议,(2) 基于32位Windows平台,(3) 不支持多处理器。 +  1.4 联赛将在“UCCI引擎联赛模拟器”(以下简称“模拟器”)这一平台上批处理运行。 + +二、比赛方式 + +  2.1 比赛分慢棋和快棋两个组别,允许参赛程序只参加其中一个组别,慢棋和快棋的排名和等级分都分开统计。 +  2.2 各参赛程序将通过一组或多组双循环赛(互先)决定排名,原则上每次比赛不少于20轮。 +  2.3 每次比赛将重新决定各程序的名次,但ELO等级分将在上次比赛结果的基础上更新。 +  2.4 原则上新的程序ELO等级分定为2000分,首次参赛K值为20,以后为10。 +  2.5 每位程序作者可以提供多个程序参加比赛,但考虑到等级分继承的问题,原则上不允许用同一程序的多个版本参赛。 + +三、比赛组织办法 + +  3.1 比赛由象棋引擎作者轮流担任东道主,提供机器并运行比赛的批处理程序。 +  3.2 比赛结束后,东道主负责公布比赛的结果(名次和等级分)和棋谱。 +  3.3 东道主的比赛机器必须用测试程序来确定标准CPU时间(大致以3000MHz的处理器为标准),以决定快棋和慢棋的时限。 +  3.4 比赛前东道主必须公布比赛机器的规格,尤其是存储器大小,原则上参赛程序占用的存储器不得超过比赛机器存储器大小的四分之一。 +  3.5 对于商业的和未发布的象棋引擎,未经程序作者同意,东道主不得擅自传播其拷贝。 + +四、比赛规则 + +  4.1 比赛采用加时制限时,原则上慢棋为30分钟标准CPU时间,每步加时10秒,快棋为10分钟标准CPU时间,每步加时3秒,不启用后台思考。 +  4.2 比赛引擎的开局库、残局库不受限制。 +  4.3 比赛引擎在空闲时间(对方思考的时间)不允许有明显占用CPU资源的行为。 +  4.4 比赛引擎出现以下情况将被判负:(1) 被将死或困毙,(2) 单方面长将或长捉同一子导致第四次出现重复局面,(3) 走出不符合规则的着法(包括送吃帅将),(4) 超时或程序崩溃。 +  4.5 出现以下情况将判和棋:(1) 超过自然回合数限制(原则上是50回合),(2) 没有单方面长将和长捉同一子的情况下第四次出现重复局面,(3) 双方均没有进攻棋子。 +  4.6 比赛前程序的作者必须将UCCI引擎选项设置告知东道主,比赛期间不得更换引擎和更改选项设置。 + +五、棋例规则 + +  5.1 电脑象棋联赛采用亚洲象棋竞赛的棋例规则,即单方面长将或长捉同一子判负,长捉多子或将捉交替判和。 +  5.2 当第四次出现重复局面时,模拟器根据重复阶段的走子进行裁定:(1) 一方长将而另一方没有长将,则长将的一方判负;(2) 一方长捉同一子而另一方没有长将和长捉同一子,则长捉同一子的一方判负;(3) 其他情况均判和。 +  5.3 为减少“长捉”在解释上的争议,使联赛的规则更透明,模拟器用最简单的实现来判断“长捉”; +  5.4 为简化规则,模拟器把“捉”定义为:(1) 马或炮攻击到车,(2) 马攻击到受保护的炮或兵(卒);(3) 炮攻击到受保护的马或兵(卒);(4) 车攻击到受保护的马、炮或兵(卒);(5) 攻击未过河的兵(卒)不算捉。 +  5.5 为简化规则,模拟器只对走完的子进行“捉”的判断,而不考虑闪击等复杂情况。 +  5.6 为简化规则,模拟器把相同兵种之间的捉(即邀兑)排除在捉的范围内,包括特殊的情况,例如不蹩足的马捉蹩足的马,以及能离开的子捉不能离开的子,尽管都构成真正意义的捉,但模拟器并不会将这两种情况识别成捉。 +  5.7 为简化规则,模拟器判断“捉”时不考虑两种特殊的情况:(1) 攻击子不能离开而不构成真正意义的捉,但模拟器仍会将其识别成捉;(2) 保护子不能离开而不构成真正意义的保护,但模拟器仍会认为该子受保护而不将其识别成捉。 +  5.8 模拟器判定某方长捉同一子作负,参赛者可在比赛结束后对模拟器的裁决提出异议,由联赛组委会决定维持原判或重赛该局,根据重赛结果对联赛积分作更正,重新排定名次。 + +六、附则 + +  6.1 “UCCI引擎联赛模拟器”的源程序公开。 +  6.2 参赛者在认可本规则及“UCCI引擎联赛模拟器”的前提下方可参加比赛。 +  6.3 参赛者有权对本规则及“UCCI引擎联赛模拟器”提出意见。 +  6.4 比赛的独家报道权归 www.elephantbase.net 网站所有。 +  6.5 本规则的解释权属于 www.elephantbase.net 网站。 + +参考资料 + +  《中国象棋通用引擎协议(UCCI协议)》。 diff --git a/LEAGUE/UCCILEAG.INI b/LEAGUE/UCCILEAG.INI new file mode 100644 index 0000000..a5847ee --- /dev/null +++ b/LEAGUE/UCCILEAG.INI @@ -0,0 +1,40 @@ +;#======================# +;$ UCCI引擎联赛配置文件 $ +;#======================# + +[League] +Event=电脑象棋联赛热身赛 +Site=上海 +Teams=14 +Processors=2 +Roundrobins=2 +InitialTime=10(分钟) +IncrementalTime=3(秒) +StoppingTime=200(毫秒) +StandardCpuTime=1000(毫秒) + +[Teams] +;缩写,引擎名称,等级分,K值,引擎文件,配置文件(可选),URL(可选) +Team=FOR,宝岛一号,2000,20,ELEEYE.EXE +Team=ELE,相眼竞技,2000,20,ELEEYE.EXE,,http://www.elephantbase.net/league/elephanteye.htm +Team=XQC,象棋旋风,2000,20,ELEEYE.EXE,,http://www.elephantbase.net/league/xqcyclone.htm +Team=EAS,棋乐无穷,2000,20,ELEEYE.EXE +Team=BIT,理治棋壮,2000,20,ELEEYE.EXE,,http://www.elephantbase.net/league/bitstronger.htm + +[Live] +Host=live.elephantbase.net +Path=/upload.php +Password=changeit +Extension=php +Counter=http://live.elephantbase.net/counter.php +Header=header.htm +Footer=footer.htm +Port=80 +Refresh=15(秒) +Interval=5000(毫秒) + +[Proxy] +ProxyHost=localhost +ProxyPort=808 +ProxyUser=root +ProxyPassword=changeit \ No newline at end of file diff --git a/LEAGUE/makefile.sh b/LEAGUE/makefile.sh new file mode 100644 index 0000000..8ddb898 --- /dev/null +++ b/LEAGUE/makefile.sh @@ -0,0 +1 @@ +g++ -DNDEBUG -O4 -Wall -ldl -oUCCILEAG.EXE ../base/pipe.cpp ../base/wsockbas.cpp ../codec/base64/base64.cpp ../eleeye/pregen.cpp ../eleeye/position.cpp ../eleeye/genmoves.cpp ../cchess/cchess.cpp ../cchess/pgnfile.cpp uccileag.cpp \ No newline at end of file diff --git a/LEAGUE/uccileag.cpp b/LEAGUE/uccileag.cpp new file mode 100644 index 0000000..99178d3 --- /dev/null +++ b/LEAGUE/uccileag.cpp @@ -0,0 +1,1478 @@ +/* +UCCILEAG - a Computer Chinese Chess League (UCCI Engine League) Emulator +Designed by Morning Yellow, Version: 3.7, Last Modified: Apr. 2008 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#ifdef _WIN32 + #include +#else + #include +#endif +#include "../base/base2.h" +#include "../base/parse.h" +#include "../base/pipe.h" +#include "../base/wsockbas.h" +#include "../codec/base64/base64.h" +#include "../eleeye/position.h" +#include "../cchess/cchess.h" +#include "../cchess/ecco.h" +#include "../cchess/pgnfile.h" + +const int MAX_CHAR = LINE_INPUT_MAX_CHAR; // 输入报告的最大行长度,同时也是引擎发送和接收信息的最大行长度 +const int MAX_ROBIN = 36; // 最多的循环 +const int MAX_TEAM = 32; // 最多的参赛队数 +const int MAX_PROCESSORS = 32; // 最多的处理器数 +const int QUEUE_LEN = 64; // 处理器队列长度(最好是处理器数的两倍) + +const char *const cszRobinChar = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +// 进程文件的记录结构 +struct CheckStruct { + int mv, nTimer; +}; // chk + +// 进程文件的控制结构 +struct CheckFileStruct { + FILE *fp; + int nLen, nPtr; + bool Eof(void) { // 判断进程文件是否读完 + return nPtr == nLen; + } + void Open(const char *szFileName); // 打开进程文件 + void Close(void) { // 关闭进程文件 + fclose(fp); + } + CheckStruct Read(void) { // 读进程文件的记录 + CheckStruct chkRecord; + fseek(fp, nPtr * sizeof(CheckStruct), SEEK_SET); + fread(&chkRecord, sizeof(CheckStruct), 1, fp); + nPtr ++; + return chkRecord; + } + void Write(CheckStruct chkRecord) { // 写进程文件的记录 + fseek(fp, nPtr * sizeof(CheckStruct), SEEK_SET); + fwrite(&chkRecord, sizeof(CheckStruct), 1, fp); + fflush(fp); + nPtr ++; + nLen ++; + } +}; + +// 打开进程文件,如果文件不存在,那么要新建一个空文件并重新打开 +void CheckFileStruct::Open(const char *szFileName) { + fp = fopen(szFileName, "r+b"); + if (fp == NULL) { + fp = fopen(szFileName, "wb"); + if (fp == NULL) { + printf("错误:无法建立比赛过程文件\"%s\"!\n", szFileName); + exit(EXIT_FAILURE); + } + fclose(fp); + fp = fopen(szFileName, "r+b"); + if (fp == NULL) { + printf("错误:无法打开比赛过程文件\"%s\"!\n", szFileName); + exit(EXIT_FAILURE); + } + nLen = nPtr = 0; + } else { + fseek(fp, 0, SEEK_END); + nLen = ftell(fp) / sizeof(CheckStruct); + nPtr = 0; + } +} + +// 参赛队结构 +struct TeamStruct { + uint32_t dwAbbr; + int nEloValue, nKValue; + char szEngineName[MAX_CHAR], szEngineFile[MAX_CHAR], szOptionFile[MAX_CHAR], szUrl[MAX_CHAR]; + int nWin, nDraw, nLoss, nScore; +}; + +// 参赛队列表 +static TeamStruct TeamList[MAX_TEAM]; + +// 联赛全局变量 +static struct { + int nTeamNum, nRobinNum, nRoundNum, nGameNum, nRemainProcs; + int nInitTime, nIncrTime, nStopTime, nStandardCpuTime, nNameLen; + bool bPromotion; + char szEvent[MAX_CHAR], szSite[MAX_CHAR]; + EccoApiStruct EccoApi; +} League; + +// 循环赛对阵图 +static char RobinTable[2 * MAX_TEAM - 2][MAX_TEAM / 2][2]; + +// 直播全局变量 +static struct { + int8_t cResult[MAX_ROBIN][2 * MAX_TEAM - 2][MAX_TEAM / 2]; + char szHost[MAX_CHAR], szPath[MAX_CHAR], szPassword[MAX_CHAR]; + char szExt[MAX_CHAR], szCounter[MAX_CHAR], szHeader[MAX_CHAR], szFooter[MAX_CHAR]; + char szProxyHost[MAX_CHAR], szProxyUser[MAX_CHAR], szProxyPassword[MAX_CHAR]; + int nPort, nRefresh, nInterval, nProxyPort; + int64_t llTime; +} Live; + +static const char *const cszContent1 = + "--[UCCI-LIVE-UPLOAD-BOUNDARY]" "\r\n" + "Content-Disposition: form-data; name=\"upload\"; filename=\"upload.txt\"" "\r\n" + "Content-Type: text/plain" "\r\n" + "\r\n"; +static const char *const cszContentFormat2 = + "\r\n" + "--[UCCI-LIVE-UPLOAD-BOUNDARY]" "\r\n" + "Content-Disposition: form-data; name=\"filename\"" "\r\n" + "\r\n" + "%s" "\r\n" + "--[UCCI-LIVE-UPLOAD-BOUNDARY]" "\r\n" + "Content-Disposition: form-data; name=\"password\"" "\r\n" + "\r\n" + "%s" "\r\n" + "--[UCCI-LIVE-UPLOAD-BOUNDARY]--" "\r\n"; +static const char *const cszPostFormat = + "POST %s HTTP/1.1" "\r\n" + "Content-Type: multipart/form-data; boundary=[UCCI-LIVE-UPLOAD-BOUNDARY]" "\r\n" + "Host: %s:%d" "\r\n" + "Content-Length: %d" "\r\n" + "\r\n"; +static const char *const cszProxyFormat = + "POST http://%s:%d%s HTTP/1.1" "\r\n" + "Content-Type: multipart/form-data; boundary=[UCCI-LIVE-UPLOAD-BOUNDARY]" "\r\n" + "Host: %s:%d" "\r\n" + "Content-Length: %d" "\r\n" + "\r\n"; +static const char *const cszAuthFormat = + "POST http://%s:%d%s HTTP/1.1" "\r\n" + "Content-Type: multipart/form-data; boundary=[UCCI-LIVE-UPLOAD-BOUNDARY]" "\r\n" + "Host: %s:%d" "\r\n" + "Content-Length: %d" "\r\n" + "Proxy-Authorization: Basic %s" "\r\n" + "\r\n"; + +static void BlockSend(int nSocket, const char *lpBuffer, int nLen, int nTimeOut) { + int nBytesWritten, nOffset; + int64_t llTime; + + nOffset = 0; + llTime = GetTime(); + while (nLen > 0 && (int) (GetTime() - llTime) < nTimeOut) { + nBytesWritten = WSBSend(nSocket, lpBuffer + nOffset, nLen); + if (nBytesWritten == 0) { + Idle(); + } else if (nBytesWritten < 0) { + return; + } + nOffset += nBytesWritten; + nLen -= nBytesWritten; + } +} + +const bool FORCE_PUBLISH = true; + +static void HttpUpload(const char *szFileName) { + FILE *fpUpload; + int nSocket, nContentLen1, nFileLen, nContentLen2, nPostLen; + char szPost[MAX_CHAR * 4], szContent2[MAX_CHAR * 4], szAuth[MAX_CHAR], szAuthB64[MAX_CHAR]; + + fpUpload = fopen(szFileName, "rb"); + if (fpUpload == NULL) { + return; + } + if (Live.nProxyPort == 0) { + nSocket = WSBConnect(Live.szHost, Live.nPort); + } else { + nSocket = WSBConnect(Live.szProxyHost, Live.nProxyPort); + } + if (nSocket == INVALID_SOCKET) { + fclose(fpUpload); + return; + } + fseek(fpUpload, 0, SEEK_END); + nContentLen1 = strlen(cszContent1); + nFileLen = ftell(fpUpload); + nContentLen2 = sprintf(szContent2, cszContentFormat2, szFileName, Live.szPassword); + if (Live.nProxyPort == 0) { + nPostLen = sprintf(szPost, cszPostFormat, Live.szPath, Live.szHost, Live.nPort, nContentLen1 + nFileLen + nContentLen2); + } else { + if (Live.szProxyUser[0] == '\0') { + nPostLen = sprintf(szPost, cszProxyFormat, Live.szHost, Live.nPort, + Live.szPath, Live.szHost, Live.nPort, nContentLen1 + nFileLen + nContentLen2); + } else { + nPostLen = sprintf(szAuth, "%s:%s", Live.szProxyUser, Live.szProxyPassword); + B64Enc(szAuthB64, szAuth, nPostLen, 0); + nPostLen = sprintf(szPost, cszAuthFormat, Live.szHost, Live.nPort, + Live.szPath, Live.szHost, Live.nPort, nContentLen1 + nFileLen + nContentLen2, szAuthB64); + } + } + // 以阻塞方式发送数据,超时为10秒,这里缓冲区最大是16K,所以网速小于1.6KB/s时就容易出错 + BlockSend(nSocket, szPost, nPostLen, 10000); + BlockSend(nSocket, cszContent1, nContentLen1, 10000); + fseek(fpUpload, 0, SEEK_SET); + while (nFileLen > 0) { + nPostLen = MIN(nFileLen, MAX_CHAR * 4); + fread(szPost, nPostLen, 1, fpUpload); + BlockSend(nSocket, szPost, nPostLen, 10000); + nFileLen -= nPostLen; + } + BlockSend(nSocket, szContent2, nContentLen2, 10000); + WSBDisconnect(nSocket); + fclose(fpUpload); +} + +static const char *const cszResultDigit[4] = { + "-", "(1-0)", "(1/2-1/2)", "(0-1)" +}; + +static bool SkipUpload(bool bForce) { + if ((int) (GetTime() - Live.llTime) < Live.nInterval) { + // 如果与上次上传间隔太近,那么暂缓上传 + if (!bForce) { + return true; + } + // 如果强制上传,那么必须等待 + while ((int) (GetTime() - Live.llTime) < Live.nInterval) { + Idle(); + } + } + Live.llTime = GetTime(); + return false; +} + +static void PrintFile(FILE *fp, const char *szFileName) { + char szLineStr[MAX_CHAR]; + char *lp; + FILE *fpEmbedded; + fpEmbedded = fopen(szFileName, "rt"); + if (fpEmbedded != NULL) { + while (fgets(szLineStr, MAX_CHAR, fpEmbedded) != NULL) { + lp = strchr(szLineStr, '\n'); + if (lp != NULL) { + *lp = '\0'; + } + fprintf(fp, "%s\n", szLineStr); + } + fclose(fpEmbedded); + } +} + +static void PublishLeague(void) { + int nSortList[MAX_TEAM]; + int i, j, k, nLastRank ,nLastScore, nResult; + uint32_t dwHome, dwAway; + TeamStruct *lpTeam; + char szEmbeddedFile[MAX_CHAR]; + char szUploadFile[16]; + FILE *fp; + + SkipUpload(FORCE_PUBLISH); // 始终返回 false + if (Live.nPort == 0) { + return; + } + strcpy(szUploadFile, "index."); + strncpy(szUploadFile + 6, Live.szExt, 6); + szUploadFile[12] = '\0'; + fp = fopen(szUploadFile, "wt"); + if (fp == NULL) { + return; + } + + // 显示页眉 + fprintf(fp, "\n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " %s 在线直播\n", League.szEvent); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, "

    \n"); + fprintf(fp, " %s 在线直播\n", League.szEvent); + fprintf(fp, "

    \n"); + if (Live.szHeader[0] != '\0') { + LocatePath(szEmbeddedFile, Live.szHeader); + PrintFile(fp, szEmbeddedFile); + } + fprintf(fp, "

    \n"); + fprintf(fp, " \n"); + fprintf(fp, " 排名\n"); + fprintf(fp, " \n"); + fprintf(fp, "

    \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + + // 显示排名,可参阅"PrintRankList()" + for (i = 0; i < League.nTeamNum; i ++) { + nSortList[i] = i; + } + for (i = 0; i < League.nTeamNum - 1; i ++) { + for (j = League.nTeamNum - 1; j > i; j --) { + if (TeamList[nSortList[j - 1]].nScore < TeamList[nSortList[j]].nScore) { + SWAP(nSortList[j - 1], nSortList[j]); + } + } + } + nLastRank = nLastScore = 0; + for (i = 0; i < League.nTeamNum; i ++) { + lpTeam = TeamList + nSortList[i]; + if (lpTeam->nScore != nLastScore) { + nLastRank = i; + nLastScore = lpTeam->nScore; + } + fprintf(fp, " \n"); + fprintf(fp, " \n", nLastRank + 1); + fprintf(fp, " \n", (const char *) &lpTeam->dwAbbr); + fprintf(fp, " \n"); + fprintf(fp, " \n", lpTeam->nEloValue); + fprintf(fp, " \n", lpTeam->nKValue); + fprintf(fp, " \n", lpTeam->nWin + lpTeam->nDraw + lpTeam->nLoss); + fprintf(fp, " \n", lpTeam->nWin); + fprintf(fp, " \n", lpTeam->nDraw); + fprintf(fp, " \n", lpTeam->nLoss); + fprintf(fp, " \n", lpTeam->nScore / 2, lpTeam->nScore % 2 == 0 ? "" : ".5"); + fprintf(fp, " \n"); + } + + // 显示内容 + fprintf(fp, "
    排名缩写引擎名称等级分K值局数积分
    %d%.3s\n"); + if (lpTeam->szUrl[0] == '\0') { + fprintf(fp, " %s\n", lpTeam->szEngineName); + } else { + fprintf(fp, " %s\n", lpTeam->szUrl, lpTeam->szEngineName); + } + fprintf(fp, " %d%d%d%d%d%d%d%s
    \n"); + fprintf(fp, "

    \n"); + fprintf(fp, " \n"); + fprintf(fp, " 赛程\n"); + fprintf(fp, " \n"); + fprintf(fp, "

    \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n", League.nGameNum); + fprintf(fp, " \n"); + + // 显示对局 + for (i = 0; i < League.nRobinNum; i ++) { + for (j = 0; j < League.nRoundNum; j ++) { + fprintf(fp, " \n"); + fprintf(fp, " \n", i * League.nRoundNum + j + 1); + for (k = 0; k < League.nGameNum; k ++) { + fprintf(fp, " \n"); + } + fprintf(fp, " \n"); + } + } + + // 显示页脚 + fprintf(fp, "
    轮次对局
    %d\n"); + nResult = Live.cResult[i][j][k]; + dwHome = TeamList[(int) RobinTable[j][k][0]].dwAbbr; + dwAway = TeamList[(int) RobinTable[j][k][1]].dwAbbr; + if (nResult == -1) { + fprintf(fp, " %.3s-%.3s\n", (const char *) &dwHome, (const char *) &dwAway); + } else { + fprintf(fp, " \n", + (const char *) &dwHome, (const char *) &dwAway, cszRobinChar[i], Live.szExt); + if (nResult == 0) { + fprintf(fp, " \n"); + fprintf(fp, " \n"); + } + fprintf(fp, " %.3s%s%.3s\n", (const char *) &dwHome, + cszResultDigit[nResult], (const char *) &dwAway); + if (nResult == 0) { + fprintf(fp, " \n"); + fprintf(fp, " \n"); + } + fprintf(fp, " \n"); + } + fprintf(fp, "
    \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, "
      
    \n"); + if (Live.szFooter[0] != '\0') { + LocatePath(szEmbeddedFile, Live.szFooter); + PrintFile(fp, szEmbeddedFile); + } + fprintf(fp, " \n"); + if (Live.szCounter[0] != '\0') { + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + } + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, "
    \n"); + fprintf(fp, " \n"); + fprintf(fp, " 您是第\n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n", Live.szCounter); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " 位观众\n"); + fprintf(fp, " \n"); + fprintf(fp, "
      
    \n"); + fprintf(fp, " \n"); + fprintf(fp, " 本页面由“" + "UCCI引擎联赛在线直播系统”生成\n"); + fprintf(fp, " \n"); + fprintf(fp, "
    \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, "
    \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " www.elephantbase.net\n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, "
    \n"); + fprintf(fp, " \n"); + fprintf(fp, "\n"); + fclose(fp); + HttpUpload(szUploadFile); +} + +static const char *const cszResultChin[4] = { + "对", "先胜", "先和", "先负" +}; + +inline void MOVE_ICCS(char *szIccs, int mv) { + szIccs[0] = (FILE_X(SRC(mv))) + 'A' - FILE_LEFT; + szIccs[1] = '9' + RANK_TOP - (RANK_Y(SRC(mv))); + szIccs[2] = '%'; + szIccs[3] = '2'; + szIccs[4] = 'D'; + szIccs[5] = (FILE_X(DST(mv))) + 'A' - FILE_LEFT; + szIccs[6] = '9' + RANK_TOP - (RANK_Y(DST(mv))); + szIccs[7] = '\0'; +} + +static void PublishGame(PgnFileStruct *lppgn, const char *szGameFile, bool bForce = false) { + int i, nStatus, nCounter; + uint64_t dqChinMove; + char szEmbeddedFile[MAX_CHAR]; + char szUploadFile[16]; + char szIccs[8]; + char *lp; + FILE *fp; + PositionStruct pos; + + if (SkipUpload(bForce)) { + return; + } + if (Live.nPort == 0) { + return; + } + strcpy(szUploadFile, szGameFile); + lp = strchr(szUploadFile, '.') + 1; + strncpy(lp, Live.szExt, 6); + lp[6] = '\0'; + fp = fopen(szUploadFile, "wt"); + if (fp == NULL) { + return; + } + + // 显示页眉 + fprintf(fp, "\n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + if (lppgn->nResult == 0 && Live.nRefresh != 0) { + fprintf(fp, " \n", Live.nRefresh, szUploadFile); + } + fprintf(fp, " %s (%s) %s - %s 在线直播\n", + lppgn->szRed, cszResultChin[lppgn->nResult], lppgn->szBlack, League.szEvent); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, "

    \n"); + fprintf(fp, " %s 在线直播\n", League.szEvent); + fprintf(fp, "

    \n"); + if (Live.szHeader[0] != '\0') { + LocatePath(szEmbeddedFile, Live.szHeader); + PrintFile(fp, szEmbeddedFile); + } + fprintf(fp, "

    \n"); + fprintf(fp, " \n"); + fprintf(fp, " %s (%s) %s\n", lppgn->szRed, cszResultChin[lppgn->nResult], lppgn->szBlack); + fprintf(fp, " \n"); + fprintf(fp, "

    \n"); + if (lppgn->nResult == 0) { + fprintf(fp, "

    \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n", szUploadFile); + fprintf(fp, " 对局进行中,如果您的浏览器没有自动跳转,请点击这里\n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, "

    \n"); + } + + // 显示Flash棋盘 + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, "
    \n"); + fprintf(fp, " 黑方 %s (%s)\n", lppgn->szBlack, lppgn->szBlackElo); + fprintf(fp, "
    \n"); + fprintf(fp, " nMaxMove; i ++) { + MOVE_ICCS(szIccs, lppgn->wmvMoveTable[i]); + fprintf(fp, "%s+", szIccs); + } + if (lppgn->nResult == 0) { + fprintf(fp, "&Step=%d", lppgn->nMaxMove); + } + fprintf(fp, "\" type=\"application/x-shockwave-flash\" pluginspage=\"http://www.macromedia.com/go/getflashplayer\" />\n"); + fprintf(fp, "
    \n"); + fprintf(fp, " 红方 %s (%s)\n", lppgn->szRed, lppgn->szRedElo); + fprintf(fp, "
    \n"); + + // 显示开局信息 + if (lppgn->szEcco[0] == '\0') { + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, "
      
    \n"); + } else { + fprintf(fp, "

    \n"); + fprintf(fp, " \n"); + if (lppgn->szVar[0] == '\0') { + fprintf(fp, " %s(%s)\n", lppgn->szOpen, lppgn->szEcco); + } else { + fprintf(fp, " %s——%s(%s)\n", lppgn->szOpen, lppgn->szVar, lppgn->szEcco); + } + fprintf(fp, " \n"); + fprintf(fp, "

    \n"); + } + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, "
    \n"); + fprintf(fp, "
    \n"); + + // 显示着法 + pos.FromFen(cszStartFen); + nCounter = 1; + for (i = 1; i <= lppgn->nMaxMove; i ++) { + dqChinMove = File2Chin(Move2File(lppgn->wmvMoveTable[i], pos), pos.sdPlayer); + if (pos.sdPlayer == 0) { + fprintf(fp, "
    %d. %.8s", nCounter, (const char *) &dqChinMove); + } else { + fprintf(fp, " %.8s
    \n", (const char *) &dqChinMove); + nCounter ++; + } + TryMove(pos, nStatus, lppgn->wmvMoveTable[i]); + if (pos.nMoveNum == MAX_MOVE_NUM) { + pos.SetIrrev(); + } + } + if (pos.sdPlayer == 1) { + fprintf(fp, "\n"); + } + if (lppgn->szCommentTable[lppgn->nMaxMove] != NULL) { + fprintf(fp, "
      %s
    \n", lppgn->szCommentTable[lppgn->nMaxMove]); + } + + // 显示页脚 + fprintf(fp, "
    \n"); + fprintf(fp, "
    \n"); + fprintf(fp, " 下载棋局", szGameFile); + fprintf(fp, "
    \n"); + fprintf(fp, "
    \n"); + fprintf(fp, "
      如果您已经安装《象棋巫师》软件,那么点击上面链接,《象棋巫师》就会自动打开棋局。
    \n"); + fprintf(fp, "
      《象棋巫师》是免费软件,您可以访问以下页面,获得速度最快的下载链接:
    \n"); + fprintf(fp, "
      (1) 《象棋巫师》简体中文版
    \n"); + fprintf(fp, "
        " + "http://www.skycn.net/soft/24665.html(天空软件站)
    \n"); + fprintf(fp, "
        " + "http://www.onlinedown.net/soft/38287.htm(华军软件园)
    \n"); + fprintf(fp, "
      (2) 《象棋巫师》繁体中文版
    \n"); + fprintf(fp, "
        " + "http://www.skycn.net/soft/35392.html(天空软件站)
    \n"); + fprintf(fp, "
        " + "http://www.onlinedown.net/soft/47666.htm(华军软件园)
    \n"); + fprintf(fp, "
    \n"); + fprintf(fp, "
      \n"); + fprintf(fp, "
    • 返回 %s 在线直播
    • \n", Live.szExt, League.szEvent); + fprintf(fp, "
    \n"); + if (Live.szFooter[0] != '\0') { + LocatePath(szEmbeddedFile, Live.szFooter); + PrintFile(fp, szEmbeddedFile); + } + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, "
    \n"); + fprintf(fp, " \n"); + fprintf(fp, " 本页面由“" + "UCCI引擎联赛在线直播系统”生成\n"); + fprintf(fp, " \n"); + fprintf(fp, "
    \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, "
    \n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, " www.elephantbase.net\n"); + fprintf(fp, " \n"); + fprintf(fp, " \n"); + fprintf(fp, "
    \n"); + fprintf(fp, " \n"); + fprintf(fp, "\n"); + fclose(fp); + HttpUpload(szUploadFile); + // 由于紧接着就上传,不能保证棋局文件一定上传成功 + HttpUpload(szGameFile); +} + +// 比赛结构,0代表主队(先行方),1代表客队(后行方) +struct GameStruct { + int sd, nCounter, nResult, nTimer[2]; + bool bTimeout, bStarted[2], bUseMilliSec[2], bDraw; + int64_t llTime; + TeamStruct *lpTeam[2]; + PipeStruct pipe[2]; + PositionStruct posIrrev; + char szIrrevFen[MAX_CHAR]; + char szGameFile[16]; + PgnFileStruct *lppgn; + uint32_t dwFileMove[20]; + FILE *fpLogFile; + CheckFileStruct CheckFile; + + void Send(const char *szLineStr) { + pipe[sd].LineOutput(szLineStr); + fprintf(fpLogFile, "Emu->%.3s(%08d):%s\n", (const char *) &lpTeam[sd]->dwAbbr, + nTimer[sd] - (int) (GetTime() - llTime), szLineStr); + fflush(fpLogFile); + } + bool Receive(char *szLineStr) { + if (pipe[sd].LineInput(szLineStr)) { + fprintf(fpLogFile, "%.3s->Emu(%08d):%s\n", (const char *) &lpTeam[sd]->dwAbbr, + nTimer[sd] - (int) (GetTime() - llTime), szLineStr); + fflush(fpLogFile); + return true; + } else { + return false; + } + } + void AddMove(int mv); // 走一个着法 + void RunEngine(void); // 让引擎运行 + void BeginGame(int nRobin, int nRound, int nGame); // 开始一个棋局 + void ResumeGame(void); // 继续上次挂起的棋局 + bool EndGame(int nRobin, int nRound, int nGame); // 终止一个棋局 +}; + +static const char *const cszColorStr[2] = { + "红方", "黑方" +}; + +const int BESTMOVE_THINKING = 0; // 引擎正在思考,没有反馈值 +const int BESTMOVE_DRAW = -1; // 引擎接受提和的反馈值 +const int BESTMOVE_RESIGN = -2; // 引擎认输的反馈值 +const int BESTMOVE_TIMEOUT = -3; // 引擎超时的反馈值 + +// 走一个着法 +void GameStruct::AddMove(int mv) { + int nStatus; + uint32_t dwEccoIndex; + char *szComment; + if (mv < BESTMOVE_THINKING) { + szComment = new char[MAX_CHAR]; + lppgn->szCommentTable[lppgn->nMaxMove] = szComment; + if (mv == BESTMOVE_DRAW) { + strcpy(szComment, "双方议和"); + nResult = 2; + } else { + sprintf(szComment, mv == BESTMOVE_RESIGN ? "%s认输" : "%s超时作负", cszColorStr[sd]); + nResult = 3 - sd * 2; + } + } else { + // 首先把该着法记录到棋谱上 + lppgn->nMaxMove ++; + lppgn->wmvMoveTable[lppgn->nMaxMove] = mv; + // 解析ECCO + if (League.EccoApi.Available()) { + if (lppgn->nMaxMove <= 20) { + dwFileMove[lppgn->nMaxMove - 1] = Move2File(mv, posIrrev); + } + dwEccoIndex = League.EccoApi.EccoIndex((const char *) dwFileMove); + strcpy(lppgn->szEcco, (const char *) &dwEccoIndex); + strcpy(lppgn->szOpen, League.EccoApi.EccoOpening(dwEccoIndex)); + strcpy(lppgn->szVar, League.EccoApi.EccoVariation(dwEccoIndex)); + } + // 然后走这个着法,并判断状态 + TryMove(posIrrev, nStatus, mv); + if ((nStatus & MOVE_CAPTURE) != 0) { + posIrrev.ToFen(szIrrevFen); + posIrrev.SetIrrev(); + } + nTimer[sd] += League.nIncrTime * League.nStandardCpuTime; + if (nStatus < MOVE_MATE) { + // 如果是正常着法,那么结果设为“进行中” + nResult = 0; + } else { + // 如果是终止着法,那么根据状态判定结果 + szComment = new char[MAX_CHAR]; + lppgn->szCommentTable[lppgn->nMaxMove] = szComment; + if (false) { + } else if ((nStatus & MOVE_ILLEGAL) != 0 || (nStatus & MOVE_INCHECK) != 0) { + sprintf(szComment, "%s走法违例", cszColorStr[sd]); + nResult = 3 - sd * 2; + } else if ((nStatus & MOVE_DRAW) != 0) { + strcpy(szComment, "超过自然限着作和"); + nResult = 2; + } else if ((nStatus & MOVE_PERPETUAL) != 0) { + if ((nStatus & MOVE_PERPETUAL_WIN) != 0) { + if ((nStatus & MOVE_PERPETUAL_LOSS) != 0) { + strcpy(szComment, "双方不变作和"); + nResult = 2; + } else { + sprintf(szComment, "%s长打作负", cszColorStr[1 - sd]); + nResult = 1 + sd * 2; + } + } else { + if ((nStatus & MOVE_PERPETUAL_LOSS) != 0) { + sprintf(szComment, "%s长打作负", cszColorStr[sd]); + nResult = 3 - sd * 2; + } else { + strcpy(szComment, "双方不变作和"); + nResult = 2; + } + } + } else { // MOVE_MATE + sprintf(szComment, "%s胜", cszColorStr[sd]); + nResult = 1 + sd * 2; + } + } + } + lppgn->nResult = nResult; + // 交换走子方,其实"sd"和"posIrrev.sdPlayer"是同步的 + sd = 1 - sd; +} + +const char *const cszGo = "go time %d increment %d opptime %d oppincrement %d"; +const char *const cszGoDraw = "go draw time %d increment %d opptime %d oppincrement %d"; + +// 让引擎运行 +void GameStruct::RunEngine(void) { + char szLineStr[MAX_CHAR], szFileName[MAX_CHAR]; + char *lpLineChar; + int i, nMoveNum, nBanMoves; + int mvBanList[MAX_GEN_MOVES]; + MoveStruct mvs[MAX_GEN_MOVES]; + uint32_t dwMoveStr; + FILE *fpOptionFile; + + if (!bStarted[sd]) { + // 如果引擎尚未启动,那么启动引擎 + llTime = GetTime(); + LocatePath(szFileName, lpTeam[sd]->szEngineFile); + pipe[sd].Open(szFileName); + Send("ucci"); + // 发送"ucci"指令后,在10秒钟内等待"ucciok"反馈信息 + while ((int) (GetTime() - llTime) < 10000) { + if (Receive(szLineStr)) { + if (StrEqv(szLineStr, "option usemillisec ")) { + bUseMilliSec[sd] = true; + } + if (StrEqv(szLineStr, "ucciok")) { + break; + } + } else { + Idle(); + } + } + // 设置必要的初始化选项 + if (League.bPromotion) { + Send("setoption promotion true"); + } else { + Send("setoption promotion false"); + } + Send("setoption ponder false"); + Send("setoption newgame"); + if (bUseMilliSec[sd]) { + Send("setoption usemillisec true"); + } + // 把引擎选项文件的内容发送给引擎 + LocatePath(szFileName, lpTeam[sd]->szOptionFile); + fpOptionFile = fopen(szFileName, "rt"); + if (fpOptionFile != NULL) { + while (fgets(szLineStr, MAX_CHAR, fpOptionFile) != NULL) { + lpLineChar = strchr(szLineStr, '\n'); + if (lpLineChar != NULL) { + *lpLineChar = '\0'; + } + Send(szLineStr); + } + fclose(fpOptionFile); + } + bStarted[sd] = true; + } + + // 向引擎发送当前局面 + llTime = GetTime(); + lpLineChar = szLineStr; + lpLineChar += sprintf(lpLineChar, "position fen %s - - 0 1", szIrrevFen); + if (posIrrev.nMoveNum > 1) { + lpLineChar += sprintf(lpLineChar, " moves"); + for (i = 1; i < posIrrev.nMoveNum; i ++) { + dwMoveStr = MOVE_COORD(posIrrev.rbsList[i].mvs.wmv); + lpLineChar += sprintf(lpLineChar, " %.4s", (const char *) &dwMoveStr); + } + } + Send(szLineStr); + + // 向引擎发送禁着指令 + nBanMoves = 0; + nMoveNum = posIrrev.GenAllMoves(mvs); + for (i = 0; i < nMoveNum; i ++) { + if (posIrrev.MakeMove(mvs[i].wmv)) { + // 如果走了合理着法,但构成长打并达到两次重复,则该着法必须设为禁着 + // 注意:现在已经轮到对方走子了,所以"REP_WIN"才表示长打 + if (posIrrev.RepStatus(2) == REP_WIN) { + mvBanList[nBanMoves] = mvs[i].wmv; + nBanMoves ++; + } + posIrrev.UndoMakeMove(); + } + } + if (nBanMoves > 0) { + lpLineChar = szLineStr; + lpLineChar += sprintf(lpLineChar, "banmoves"); + for (i = 0; i < nBanMoves; i ++) { + dwMoveStr = MOVE_COORD(mvBanList[i]); + lpLineChar += sprintf(lpLineChar, " %.4s", (const char *) &dwMoveStr); + } + Send(szLineStr); + } + + // 向引擎发送走棋指令:"go [draw] time %d increment %d opptime %d oppincrement %d"; + if (bUseMilliSec[sd]) { + sprintf(szLineStr, bDraw ? cszGoDraw : cszGo, nTimer[sd], + League.nIncrTime * League.nStandardCpuTime, nTimer[1 - sd], League.nIncrTime * League.nStandardCpuTime); + } else { + sprintf(szLineStr, bDraw ? cszGoDraw : cszGo, nTimer[sd] / 1000, + League.nIncrTime * League.nStandardCpuTime / 1000, nTimer[1 - sd] / 1000, + League.nIncrTime * League.nStandardCpuTime / 1000); + } + Send(szLineStr); + bTimeout = false; +} + +// 开始一个棋局 +void GameStruct::BeginGame(int nRobin, int nRound, int nGame) { + int i; + char szFileName[16]; + CheckStruct chkRecord; + time_t dwTime; + tm *lptm; + + Live.cResult[nRobin][nRound][nGame] = 0; + PublishLeague(); + League.nRemainProcs --; // 将剩余可用处理器数量减一 + time(&dwTime); + lptm = localtime(&dwTime); // 获得时间 + lpTeam[0] = TeamList + RobinTable[nRound][nGame][0]; + lpTeam[1] = TeamList + RobinTable[nRound][nGame][1]; + sd = nCounter = nResult = 0; + nTimer[0] = nTimer[1] = League.nInitTime * League.nStandardCpuTime * 60; + bStarted[0] = bStarted[1] = bUseMilliSec[0] = bUseMilliSec[1] = bDraw = false; + strcpy(szIrrevFen, cszStartFen); + posIrrev.FromFen(szIrrevFen); + + // 合成棋谱文件 + lppgn = new PgnFileStruct(); + lppgn->posStart = posIrrev; + sprintf(szGameFile, "%.3s-%.3s%c.PGN", (const char *) &lpTeam[0]->dwAbbr, + (const char *) &lpTeam[1]->dwAbbr, cszRobinChar[nRobin]); + strcpy(lppgn->szEvent, League.szEvent); + sprintf(lppgn->szRound, "%d", nRobin * League.nRoundNum + nRound + 1); + sprintf(lppgn->szDate, "%04d.%02d.%02d", lptm->tm_year + 1900, lptm->tm_mon + 1, lptm->tm_mday); + strcpy(lppgn->szSite, League.szSite); + strcpy(lppgn->szRed, lpTeam[0]->szEngineName); + sprintf(lppgn->szRedElo, "%d", lpTeam[0]->nEloValue); + strcpy(lppgn->szBlack, lpTeam[1]->szEngineName); + sprintf(lppgn->szBlackElo, "%d", lpTeam[1]->nEloValue); + for (i = 0; i < 20; i ++) { + dwFileMove[i] = 0; + } + + // 打开日志文件和进程文件 + sprintf(szFileName, "%.3s-%.3s%c.LOG", (const char *) &lpTeam[0]->dwAbbr, + (const char *) &lpTeam[1]->dwAbbr, cszRobinChar[nRobin]); + fpLogFile = fopen(szFileName, "at"); + if (fpLogFile == NULL) { + printf("错误:无法建立日志文件\"%s\"!\n", szFileName); + exit(EXIT_FAILURE); + } + sprintf(szFileName, "%.3s-%.3s%c.CHK", (const char *) &lpTeam[0]->dwAbbr, + (const char *) &lpTeam[1]->dwAbbr, cszRobinChar[nRobin]); + CheckFile.Open(szFileName); + + // 如果进程文件有记录,那么先解析进程记录的着法 + while (!CheckFile.Eof()) { + chkRecord = CheckFile.Read(); + nTimer[sd] = chkRecord.nTimer; + AddMove(chkRecord.mv); + if (nResult > 0) { + lppgn->Write(szGameFile); + PublishGame(lppgn, szGameFile, FORCE_PUBLISH); + League.nRemainProcs ++; // 在EndGame()之前就释放处理器资源,提高处理器利用率 + return; // 如果棋局结束(进程文件是完整的),那么就不必启动引擎了 + } + } + lppgn->Write(szGameFile); + PublishGame(lppgn, szGameFile); + RunEngine(); // 进程文件读完后(进程文件不完整),就需要调用引擎了 + // 向引擎发送指令后,棋局就挂起,等待下次调用"ResumeGame()"以继续进行 +} + +// 继续上次挂起的棋局 +void GameStruct::ResumeGame(void) { + char szLineStr[MAX_CHAR]; + CheckStruct chkRecord; + char *lp; + + if (nResult == 0) { // 棋局尚未结束时才有操作 + // 首先读取引擎反馈信息 + chkRecord.mv = BESTMOVE_THINKING; + while (Receive(szLineStr)) { + lp = szLineStr; + if (StrEqvSkip(lp, "bestmove ")) { + chkRecord.mv = COORD_MOVE(*(uint32_t *) lp); + lp += 4; + if (StrScan(lp, " resign")) { + chkRecord.mv = BESTMOVE_RESIGN; + } else { + if (StrScan(lp, " draw")) { + if (bDraw) { + chkRecord.mv = BESTMOVE_DRAW; + } else { + bDraw = true; + } + } else { + bDraw = false; + }; + }; + break; + } else if (StrEqv(lp, "nobestmove")) { + chkRecord.mv = BESTMOVE_RESIGN; + break; + } + } + // 如果没有读到反馈着法,就判断引擎是否超时 + if (chkRecord.mv == BESTMOVE_THINKING) { + if (bTimeout) { + if ((int) (GetTime() - llTime) > nTimer[sd] + League.nStopTime) { + chkRecord.mv = BESTMOVE_TIMEOUT; // 只有时间超出停止时间后,才给空着以表示超时 + } + } else { + if ((int) (GetTime() - llTime) > nTimer[sd]) { + Send("stop"); + bTimeout = true; + } + } + } + // 如果有反馈着法(包括超时返回的空着),就走这个着法 + if (chkRecord.mv != BESTMOVE_THINKING) { + nTimer[sd] -= (int) (GetTime() - llTime); + if (nTimer[sd] < 0) { + nTimer[sd] = 0; + } + chkRecord.nTimer = nTimer[sd]; + CheckFile.Write(chkRecord); + AddMove(chkRecord.mv); + lppgn->Write(szGameFile); + PublishGame(lppgn, szGameFile, nResult > 0); + if (nResult == 0) { + RunEngine(); // 如果棋局尚未结束,那么让引擎思考下一步棋 + } else { + // 如果棋局已经结束,那么终止两个引擎 + for (sd = 0; sd < 2; sd ++) { + if (bStarted[sd]) { + llTime = GetTime(); + Send("quit"); + while ((int) (GetTime() - llTime) < 1000) { + if (Receive(szLineStr)) { + if (StrEqv(szLineStr, "bye")) { + break; + } + } else { + Idle(); + } + } + pipe[sd].Close(); + } + } + League.nRemainProcs ++; // 在EndGame()之前就释放处理器资源,提高处理器利用率 + } + } + } +} + +const struct ResultStruct { + int nHomeWin, nHomeDraw, nHomeLoss, nHomeScore, nAwayWin, nAwayDraw, nAwayLoss, nAwayScore; + double dfWHome; + const char *szResultStr; +} ResultList[4] = { + {0, 0, 0, 0, 0, 0, 0, 0, 0.0, " "}, + {1, 0, 0, 2, 0, 0, 1, 0, 1.0, " 1-0 "}, + {0, 1, 0, 1, 0, 1, 0, 1, 0.5, "1/2-1/2"}, + {0, 0, 1, 0, 1, 0, 0, 2, 0.0, " 0-1 "} +}; + +inline void PrintDup(int nChar, int nDup) { + int i; + for (i = 0; i < nDup; i ++) { + putchar(nChar); + } +} + +// 终止一个棋局 +bool GameStruct::EndGame(int nRobin, int nRound, int nGame) { + double dfWeHome; + const ResultStruct *lpResult; + + if (nResult > 0) { + delete lppgn; + fclose(fpLogFile); + CheckFile.Close(); + // 如果棋局已经完成,那么计算成绩 + dfWeHome = 1.0 / (1.0 + pow(10.0, (double) (lpTeam[1]->nEloValue - lpTeam[0]->nEloValue) / 400.0)); + lpResult = ResultList + nResult; + lpTeam[0]->nWin += lpResult->nHomeWin; + lpTeam[0]->nDraw += lpResult->nHomeDraw; + lpTeam[0]->nLoss += lpResult->nHomeLoss; + lpTeam[0]->nScore += lpResult->nHomeScore; + lpTeam[1]->nWin += lpResult->nAwayWin; + lpTeam[1]->nDraw += lpResult->nAwayDraw; + lpTeam[1]->nLoss += lpResult->nAwayLoss; + lpTeam[1]->nScore += lpResult->nAwayScore; + lpTeam[0]->nEloValue += (int) ((lpResult->dfWHome - dfWeHome) * lpTeam[0]->nKValue); + lpTeam[1]->nEloValue += (int) ((dfWeHome - lpResult->dfWHome) * lpTeam[1]->nKValue); + // 每轮的第一局棋显示轮次 + if (nGame == 0) { + printf("第 %d 轮:\n\n", nRobin * League.nRoundNum + nRound + 1); + } + // 输出棋局结果 + printf("%s", lpTeam[0]->szEngineName); + PrintDup(' ', League.nNameLen - strlen(lpTeam[0]->szEngineName)); + printf(" %s %s", lpResult->szResultStr, lpTeam[1]->szEngineName); + PrintDup(' ', League.nNameLen - strlen(lpTeam[1]->szEngineName)); + printf(" (%.3s-%.3s%c.PGN)\n", (const char *) &lpTeam[0]->dwAbbr, + (const char *) &lpTeam[1]->dwAbbr, cszRobinChar[nRobin]); + fflush(stdout); + // 整理直播 + Live.cResult[nRobin][nRound][nGame] = nResult; + PublishLeague(); + return true; + } else { + return false; + } +} + +// 输出排名表 +static void PrintRankList(void) { + int i, j, nLastRank, nLastScore; + int nSortList[MAX_TEAM]; + TeamStruct *lpTeam; + // 输出表头 + printf(" 缩写 引擎名称"); + PrintDup(' ', League.nNameLen - 8); + printf(" ELO K 胜 和 负 积分\n"); + PrintDup('=', League.nNameLen - 8); + printf("================" "==========================\n"); + for (i = 0; i < League.nTeamNum; i ++) { + nSortList[i] = i; + } + // 用冒泡排序法按积分排序 + for (i = 0; i < League.nTeamNum - 1; i ++) { + for (j = League.nTeamNum - 1; j > i; j --) { + if (TeamList[nSortList[j - 1]].nScore < TeamList[nSortList[j]].nScore) { + SWAP(nSortList[j - 1], nSortList[j]); + } + } + } + // 依次显示名次,如果积分和前一个队相同,那么名次也和前一个队相同 + nLastRank = nLastScore = 0; + for (i = 0; i < League.nTeamNum; i ++) { + lpTeam = TeamList + nSortList[i]; + if (lpTeam->nScore != nLastScore) { + nLastRank = i; + nLastScore = lpTeam->nScore; + } + printf("%2d %.3s %s", nLastRank + 1, (const char *) &lpTeam->dwAbbr, lpTeam->szEngineName); + PrintDup(' ', League.nNameLen - strlen(lpTeam->szEngineName)); + printf(" %4d %2d %3d %3d %3d %3d%s\n", lpTeam->nEloValue, lpTeam->nKValue, lpTeam->nWin, + lpTeam->nDraw, lpTeam->nLoss, lpTeam->nScore / 2, lpTeam->nScore % 2 == 0 ? "" : ".5"); + } + PrintDup('=', League.nNameLen - 8); + printf("================" "==========================\n\n"); + // " 缩写 引擎名称" " ELO K 胜 和 负 积分" +} + +// 棋局队列占用大量空间,所以必须用全局变量 +static GameStruct GameList[QUEUE_LEN]; + +// 主例程 +int main(void) { + // 以下变量牵涉输入报告的读取 + char szLineStr[MAX_CHAR]; + char *lp; + FILE *fpIniFile; + TeamStruct *lpTeam; + int i, j, k, nSocket; + int nEngineFileLen; // 引擎文件的最大长度 + // 以下变量牵涉到棋局队列的控制 + int nRobinPush, nRoundPush, nGamePush; + int nRobinPop, nRoundPop, nGamePop; + int nQueueBegin, nQueueEnd, nQueueIndex; + + // 首先是读取输入报告 + League.nTeamNum = League.nInitTime = League.nIncrTime = League.nStopTime = 0; + League.nRemainProcs = League.nRobinNum = 1; + League.nStandardCpuTime = 1000; + League.nNameLen = nEngineFileLen = 8; // 引擎名称和引擎文件的最小长度 + League.bPromotion = false; + League.szEvent[0] = League.szSite[0] = '\0'; + Live.szHost[0] = Live.szPath[0] = Live.szPassword[0] = Live.szCounter[0] = '\0'; + Live.szProxyHost[0] = Live.szProxyUser[0] = Live.szProxyPassword[0] = '\0'; + strcpy(Live.szExt, "htm"); + Live.nPort = Live.nProxyPort = 80; + Live.nRefresh = Live.nInterval = 0; + + LocatePath(szLineStr, "UCCILEAG.INI"); + fpIniFile = fopen(szLineStr, "rt"); + if (fpIniFile == NULL) { + printf("错误:无法打开配置文件\"%s\"!\n", szLineStr); + return 0; + } + while (fgets(szLineStr, MAX_CHAR, fpIniFile) != NULL) { + StrCutCrLf(szLineStr); + lp = szLineStr; + if (false) { + } else if (StrEqvSkip(lp, "Event=")) { + strcpy(League.szEvent, lp); + } else if (StrEqvSkip(lp, "Site=")) { + strcpy(League.szSite, lp); + } else if (StrEqvSkip(lp, "Roundrobins=")) { + League.nRobinNum = Str2Digit(lp, 1, MAX_ROBIN); + } else if (StrEqvSkip(lp, "Processors=")) { + League.nRemainProcs = Str2Digit(lp, 1, MAX_PROCESSORS); + } else if (StrEqvSkip(lp, "InitialTime=")) { + League.nInitTime = Str2Digit(lp, 1, 500); + } else if (StrEqvSkip(lp, "IncrementalTime=")) { + League.nIncrTime = Str2Digit(lp, 0, 500); + } else if (StrEqvSkip(lp, "StoppingTime=")) { + League.nStopTime = Str2Digit(lp, 0, 500); + } else if (StrEqvSkip(lp, "StandardCpuTime=")) { + League.nStandardCpuTime = Str2Digit(lp, 100, 10000); + } else if (StrEqvSkip(lp, "Promotion=")) { + if (StrEqv(lp, "True")) { + League.bPromotion = true; + } else if (StrEqv(lp, "On")) { + League.bPromotion = true; + } + } else if (StrEqvSkip(lp, "Team=")) { + if (League.nTeamNum < MAX_TEAM) { + lpTeam = TeamList + League.nTeamNum; + lpTeam->dwAbbr = *(uint32_t *) lp; + StrSplitSkip(lp, ','); + StrSplitSkip(lp, ',', lpTeam->szEngineName); + League.nNameLen = MAX(League.nNameLen, (int) strlen(lpTeam->szEngineName)); + lpTeam->nEloValue = Str2Digit(lp, 0, 9999); + StrSplitSkip(lp, ','); + lpTeam->nKValue = Str2Digit(lp, 0, 99); + StrSplitSkip(lp, ','); + StrSplitSkip(lp, ',', lpTeam->szEngineFile); + nEngineFileLen = MAX(nEngineFileLen, (int) strlen(lpTeam->szEngineFile)); + StrSplitSkip(lp, ',', lpTeam->szOptionFile); + StrSplitSkip(lp, ',', lpTeam->szUrl); + League.nTeamNum ++; + } + // 以下参数只跟转播有关 + } else if (StrEqvSkip(lp, "Host=")) { + strcpy(Live.szHost, lp); // 直播主机 + } else if (StrEqvSkip(lp, "Path=")) { + strcpy(Live.szPath, lp); // 上传页面路径 + } else if (StrEqvSkip(lp, "Password=")) { + strcpy(Live.szPassword, lp); // 上传页面口令 + } else if (StrEqvSkip(lp, "Extension=")) { + strcpy(Live.szExt, lp); // 上传文件后缀 + } else if (StrEqvSkip(lp, "Counter=")) { + strcpy(Live.szCounter, lp); // 计数器路径 + } else if (StrEqvSkip(lp, "Header=")) { + strcpy(Live.szHeader, lp); // 页眉路径 + } else if (StrEqvSkip(lp, "Footer=")) { + strcpy(Live.szFooter, lp); // 页脚路径 + } else if (StrEqvSkip(lp, "Port=")) { + Live.nPort = Str2Digit(lp, 1, 65535); // HTTP端口 + } else if (StrEqvSkip(lp, "Refresh=")) { + Live.nRefresh = Str2Digit(lp, 0, 60); // 页面自动刷新时间(秒) + } else if (StrEqvSkip(lp, "Interval=")) { + Live.nInterval = Str2Digit(lp, 0, 60000); // 上传文件间隔时间(毫秒) + } else if (StrEqvSkip(lp, "ProxyHost=")) { + strcpy(Live.szProxyHost, lp); // 代理主机 + } else if (StrEqvSkip(lp, "ProxyPort=")) { + Live.nProxyPort = Str2Digit(lp, 1, 65535); // 代理端口 + } else if (StrEqvSkip(lp, "ProxyUser=")) { + strcpy(Live.szProxyUser, lp); // 代理用户名 + Live.szProxyUser[1024] = '\0'; + } else if (StrEqvSkip(lp, "ProxyPassword=")) { + strcpy(Live.szProxyPassword, lp); // 代理口令 + Live.szProxyPassword[1024] = '\0'; + } + } + fclose(fpIniFile); + if (League.nTeamNum < 2) { + printf("错误:至少需要两个参赛队!\n"); + return 0; + } + printf("#======================#\n"); + printf("$ UCCI引擎联赛输出报告 $\n"); + printf("#======================#\n\n"); + printf("赛事:  %s\n", League.szEvent); + printf("地点:  %s\n", League.szSite); + printf("参赛队数:%d\n", League.nTeamNum); + printf("处理器数:%d\n", League.nRemainProcs); + printf("循环次数:%d\n", League.nRobinNum); + printf("初始时间:%-4d 分钟\n", League.nInitTime); + printf("每步加时:%-4d 秒\n", League.nIncrTime); + printf("停止时间:%-4d 毫秒\n", League.nStopTime); + printf("换算比例:%-4d 毫秒\n", League.nStandardCpuTime); + if (League.bPromotion) { + printf("规则:  允许仕(士)相(象)升变成兵(卒)\n"); + } + printf("模拟器: UCCI引擎联赛模拟器 3.7\n\n"); + printf("参赛引擎:\n\n"); + printf(" 缩写 引擎名称"); + PrintDup(' ', League.nNameLen - 8); + printf(" ELO K 引擎程序"); + PrintDup(' ', nEngineFileLen - 8); + printf(" 配置文件\n"); + PrintDup('=', League.nNameLen + nEngineFileLen - 16); + printf("================" "=================" "============\n"); + for (i = 0; i < League.nTeamNum; i ++) { + lpTeam = TeamList + i; + printf("%2d %.3s %s", i + 1, (const char *) &lpTeam->dwAbbr, lpTeam->szEngineName); + PrintDup(' ', League.nNameLen - strlen(lpTeam->szEngineName)); + printf(" %4d %2d %s", lpTeam->nEloValue, lpTeam->nKValue, lpTeam->szEngineFile); + PrintDup(' ', nEngineFileLen - strlen(lpTeam->szEngineFile)); + printf(" %s\n", lpTeam->szOptionFile); + } + PrintDup('=', League.nNameLen + nEngineFileLen - 16); + printf("================" "=================" "============\n\n"); + // " 缩写 引擎名称" " ELO K 引擎文件" " 配置文件" + + // 接下来生成循环赛对阵表,可参阅:http://www.elephantbase.net/protocol/roundrobin.htm + League.nGameNum = (League.nTeamNum + 1) / 2; + League.nRoundNum = League.nGameNum * 2 - 1; + for (i = 0; i < League.nGameNum; i ++) { + RobinTable[0][i][0] = i; + RobinTable[0][i][1] = League.nGameNum * 2 - 1 - i; + } + for (i = 1; i < League.nRoundNum; i ++) { + RobinTable[i][0][1] = League.nGameNum * 2 - 1; + for (j = 0; j < League.nGameNum - 1; j ++) { + RobinTable[i][j][0] = RobinTable[i - 1][League.nGameNum - 1 - j][1]; + RobinTable[i][j + 1][1] = RobinTable[i - 1][League.nGameNum - 1 - j][0]; + } + RobinTable[i][League.nGameNum - 1][0] = RobinTable[i - 1][0][0]; + } + if (League.nTeamNum % 2 == 0) { + for (i = 0; i < League.nRoundNum; i ++) { + if (i % 2 != 0) { + SWAP(RobinTable[i][0][0], RobinTable[i][0][1]); + } + } + } else { + League.nGameNum --; + for (i = 0; i < League.nRoundNum; i ++) { + for (j = 0; j < League.nGameNum; j ++) { + RobinTable[i][j][0] = RobinTable[i][j + 1][0]; + RobinTable[i][j][1] = RobinTable[i][j + 1][1]; + } + } + } + for (i = 0; i < League.nRoundNum; i ++) { + for (j = 0; j < League.nGameNum; j ++) { + RobinTable[League.nRoundNum + i][j][0] = RobinTable[i][j][1]; + RobinTable[League.nRoundNum + i][j][1] = RobinTable[i][j][0]; + } + } + League.nRoundNum *= 2; + printf("赛程表:\n\n"); + printf("轮次 对局\n"); + printf("====="); + for (i = 0; i < League.nGameNum; i ++) { + printf("========"); + } + printf("\n"); + for (i = 0; i < League.nRobinNum; i ++) { + for (j = 0; j < League.nRoundNum; j ++) { + printf("%3d ", i * League.nRoundNum + j + 1); + for (k = 0; k < League.nGameNum; k ++) { + printf(" %.3s-%.3s", (const char *) &TeamList[(int) RobinTable[j][k][0]].dwAbbr, + (const char *) &TeamList[(int) RobinTable[j][k][1]].dwAbbr); + Live.cResult[i][j][k] = -1; + } + printf("\n"); + } + } + printf("====="); + for (i = 0; i < League.nGameNum; i ++) { + printf("========"); + } + printf("\n\n"); + + // 初始化ECCO解析函数 + LocatePath(szLineStr, cszLibEccoFile); + League.EccoApi.Startup(szLineStr); + + // 测试直播页面 + WSBStartup(); + nSocket = (Live.szProxyHost[0] == '\0' ? INVALID_SOCKET : WSBConnect(Live.szProxyHost, Live.nProxyPort)); + if (nSocket == INVALID_SOCKET) { + Live.nProxyPort = 0; + nSocket = (Live.szHost[0] == '\0' ? INVALID_SOCKET : WSBConnect(Live.szHost, Live.nPort)); + if (nSocket == INVALID_SOCKET) { + Live.nPort = 0; + } else { + WSBDisconnect(nSocket); + } + } else { + WSBDisconnect(nSocket); + } + Live.llTime = GetTime(); + + // 现在开始控制棋局队列,这是本模拟器的核心部分 + printf("=== 联赛进程开始 ===\n\n"); + fflush(stdout); + PreGenInit(); + ChineseInit(); + PreEval.bPromotion = League.bPromotion; + nRobinPush = nRoundPush = nGamePush = 0; // 压入队列的循环、轮次和棋局序号 + nRobinPop = nRoundPop = nGamePop = 0; // 弹出队列的循环、轮次和棋局序号 + nQueueBegin = nQueueEnd = 0; // 队列出口和队列入口 + while (nRobinPop < League.nRobinNum) { + // 把一个棋局压入队列的条件是:(1) 所有比赛完成,(2) 有剩余的处理器,(3) 队列未被填满 + if (nRobinPush < League.nRobinNum && League.nRemainProcs > 0 && (nQueueEnd + 1) % QUEUE_LEN != nQueueBegin) { + GameList[nQueueEnd].BeginGame(nRobinPush, nRoundPush, nGamePush); + nQueueEnd = (nQueueEnd + 1) % QUEUE_LEN; + // 已把一个棋局压入队列,修改循环、轮次和棋局序号 + nGamePush ++; + if (nGamePush == League.nGameNum) { + nGamePush = 0; + nRoundPush ++; + if (nRoundPush == League.nRoundNum) { + nRoundPush = 0; + nRobinPush ++; + } + } + } + + // 调度队列中的每个棋局,相当于用轮转方式管理多个进程 + nQueueIndex = nQueueBegin; + while (nQueueIndex != nQueueEnd) { + GameList[nQueueIndex].ResumeGame(); + nQueueIndex = (nQueueIndex + 1) % QUEUE_LEN; + } + + // 如果队列不是空的,那么尝试将棋局弹出队列 + if (nQueueBegin != nQueueEnd) { + if (GameList[nQueueBegin].EndGame(nRobinPop, nRoundPop, nGamePop)) { + nQueueBegin = (nQueueBegin + 1) % QUEUE_LEN; + // 已把一个棋局弹出队列,修改循环、轮次和棋局序号 + nGamePop ++; + if (nGamePop == League.nGameNum) { + // 如果一轮结束,那么输出排名表 + printf("\n"); + printf("第 %d 轮后排名:\n\n", nRobinPop * League.nRoundNum + nRoundPop + 1); + PrintRankList(); + fflush(stdout); + nGamePop = 0; + nRoundPop ++; + if (nRoundPop == League.nRoundNum) { + nRoundPop = 0; + nRobinPop ++; + } + } + } + } + Idle(); + } + printf("=== 联赛进程结束 ===\n\n"); + printf("最终排名:\n\n"); + PrintRankList(); + + WSBCleanup(); + League.EccoApi.Shutdown(); + return 0; +} diff --git a/LGPL.TXT b/LGPL.TXT new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/LGPL.TXT @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/MISC/EXPIPE/EXPIPE.BAS b/MISC/EXPIPE/EXPIPE.BAS new file mode 100644 index 0000000..c7794ab --- /dev/null +++ b/MISC/EXPIPE/EXPIPE.BAS @@ -0,0 +1,24 @@ +Attribute VB_Name = "mdlExPipe" +Option Explicit + +Sub Main() + +Dim lpsz As Long, sz As String, pipe As PipeStruct +StdioOpen +PipeOpen pipe +Do + lpsz = PipeLineInput(pipe) + If lpsz = 0 Then + Sleep 1 + Else + sz = MkBStr(lpsz) + If sz = "" Then + Exit Do + Else + PipeLineOutput pipe, sz + End If + End If +Loop +StdioClose + +End Sub diff --git a/MISC/EXPIPE/EXPIPE.VBP b/MISC/EXPIPE/EXPIPE.VBP new file mode 100644 index 0000000..eabe954 --- /dev/null +++ b/MISC/EXPIPE/EXPIPE.VBP @@ -0,0 +1,36 @@ +Type=Exe +Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\WINDOWS\system32\stdole2.tlb#OLE Automation +Module=mdlExPipe; EXPIPE.BAS +Module=mdlPipe; ..\..\utility\PIPE.BAS +Module=mdlBase; ..\..\utility\BASE.BAS +Startup="Sub Main" +ExeName32="EXPIPE.EXE" +Path32="..\..\BIN" +Command32="" +Name="prjExPipe" +HelpContextID="0" +CompatibleMode="0" +MajorVer=1 +MinorVer=0 +RevisionVer=0 +AutoIncrementVer=0 +ServerSupportFiles=0 +VersionCompanyName="Unknown Organization" +CompilationType=0 +OptimizationType=0 +FavorPentiumPro(tm)=0 +CodeViewDebugInfo=0 +NoAliasing=0 +BoundsCheck=0 +OverflowCheck=0 +FlPointCheck=0 +FDIVCheck=0 +UnroundedFP=0 +StartMode=0 +Unattended=0 +Retained=0 +ThreadPerObject=0 +MaxNumberOfThreads=1 + +[MS Transaction Server] +AutoRefresh=1 diff --git a/MISC/EXPIPE/ExPipe.java b/MISC/EXPIPE/ExPipe.java new file mode 100644 index 0000000..b47e723 --- /dev/null +++ b/MISC/EXPIPE/ExPipe.java @@ -0,0 +1,18 @@ +public class ExPipe { + public static void main(String[] args) throws Exception { + String sz; + Pipe pipe = new Pipe(); + pipe.open(null); + while (true) { + sz = pipe.lineInput(); + if (sz == null) { + Thread.sleep(1); + } else if (sz.length() == 0) { + break; + } else { + pipe.lineOutput(sz); + } + } + pipe.close(); + } +} \ No newline at end of file diff --git a/MISC/EXPIPE/Pipe.java b/MISC/EXPIPE/Pipe.java new file mode 100644 index 0000000..712cad9 --- /dev/null +++ b/MISC/EXPIPE/Pipe.java @@ -0,0 +1,69 @@ +import java.io.*; + +public class Pipe { + private static final int LINE_INPUT_MAX_CHAR = 4096; + private int nReadEnd = 0; + private InputStream in = null; + private PrintStream out = null; + private byte[] ucBuffer = new byte[LINE_INPUT_MAX_CHAR]; + + public void open(String szCommand) throws IOException { + if (szCommand == null) { + in = System.in; + out = System.out; + } else { + String[] sz = {szCommand}; + ProcessBuilder pb = new ProcessBuilder(sz); + pb.redirectErrorStream(true); + Process p = pb.start(); + in = p.getInputStream(); + out = new PrintStream(p.getOutputStream()); + } + } + + public void close() throws IOException { + nReadEnd = 0; + in.close(); + out.close(); + } + + public void lineOutput(String sz) { + out.println(sz); + out.flush(); + } + + public String lineInput() throws IOException { + String sz = getBuffer(); + if (sz == null && in.available() > 0) { + nReadEnd += in.read(ucBuffer, nReadEnd, LINE_INPUT_MAX_CHAR - nReadEnd); + sz = getBuffer(); + if (sz == null && nReadEnd == LINE_INPUT_MAX_CHAR) { + sz = new String(ucBuffer, 0, LINE_INPUT_MAX_CHAR - 1); + ucBuffer[0] = ucBuffer[LINE_INPUT_MAX_CHAR - 1]; + nReadEnd = 1; + } + } + return sz; + } + + private String getBuffer() { + String sz = null; + int nFeedEnd; + for (nFeedEnd = 0; nFeedEnd < nReadEnd; nFeedEnd ++) { + if (ucBuffer[nFeedEnd] == '\n') { + break; + } + } + if (nFeedEnd < nReadEnd) { + sz = new String(ucBuffer, 0, nFeedEnd); + int nStrChr = sz.indexOf('\r'); + if (nStrChr >= 0) { + sz = sz.substring(0, nStrChr); + } + nFeedEnd ++; + nReadEnd -= nFeedEnd; + System.arraycopy(ucBuffer, nFeedEnd, ucBuffer, 0, nReadEnd); + } + return sz; + } +} diff --git a/MISC/EXPIPE/runjar.cpp b/MISC/EXPIPE/runjar.cpp new file mode 100644 index 0000000..1834ac1 --- /dev/null +++ b/MISC/EXPIPE/runjar.cpp @@ -0,0 +1,47 @@ +#ifdef _WIN32 + #include + #include +#else + #include +#endif +#include "../../base/base2.h" + +int main(void) { + char *lpJreHome, *lpPath; + char szJarFile[1024]; + + LocatePath(szJarFile, "startup.jar"); + +#ifdef _WIN32 + + char szCommand[1024]; + sprintf(szCommand, "java -jar \"%s\"", szJarFile); + + DWORD dw; + STARTUPINFO si; + PROCESS_INFORMATION pi; + si.cb = sizeof(STARTUPINFO); + si.lpReserved = si.lpDesktop = si.lpTitle = NULL; + si.dwFlags = STARTF_USESTDHANDLES; + si.cbReserved2 = 0; + si.lpReserved2 = NULL; + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + CreateProcess(NULL, (LPSTR) szCommand, NULL, NULL, TRUE, + (GetConsoleMode(si.hStdInput, &dw) ? 0 : DETACHED_PROCESS) | + CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi); + WaitForSingleObject(pi.hProcess, INFINITE); + GetExitCodeProcess(pi.hProcess, &dw); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return dw; + +#else + + execlp("java", "java", "-jar", szJarFile, NULL); + +#endif + +} diff --git a/MISC/EXPIPE/startup.jar b/MISC/EXPIPE/startup.jar new file mode 100644 index 0000000..4624d51 Binary files /dev/null and b/MISC/EXPIPE/startup.jar differ diff --git a/MISC/PHPCHAT/PHPCHATC.BAS b/MISC/PHPCHAT/PHPCHATC.BAS new file mode 100644 index 0000000..431d33e --- /dev/null +++ b/MISC/PHPCHAT/PHPCHATC.BAS @@ -0,0 +1,220 @@ +Attribute VB_Name = "mdlPHPChat" +' PHPChat Client - A NetMeeting Client Program +' Designed by Morning Yellow, Version: 1.0, Last Modified: Sep. 2006 +' Copyright (C) 2004-2006 www.elephantbase.net +' +' This program is free software; you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation; either version 2 of the License, or +' (at your option) any later version. + +' 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. + +Option Explicit + +Public Const BUFFER_LEN As Long = 65536 + +Public Set_szServerUrl As String, Set_szChatRoom As String, Set_szMyName As String +Public Set_nRefresh As Long, Set_bFocus As Boolean, Set_bBeep As Boolean +Public Set_bLogon As Boolean, Set_bSend As Boolean, Set_bLogoff As Boolean +Public Set_szLogon As String, Set_szSend As String, Set_szLogoff As String +Public Set_bMsgLogs As Boolean, Set_nMessageId As Long + +Public App_szPath As String, App_bLogon As Boolean, App_bCtrlEnter As Boolean +Public App_bUpdated As Boolean, App_dfLastTime As Double + +Public Function Receive() As Boolean + +Dim szReceived As String, szShow As String, szMessage As String +Dim nLogFileNo As Integer, bResponse As Boolean, m As Long, n As Long +szReceived = "" +On Error Resume Next +szReceived = frmPHPChat.inet.OpenURL(Set_szServerUrl + "list_message.php?room=" + UrlEncode(Set_szChatRoom) + "&message_id=" + LTrim(Set_nMessageId)) +On Error GoTo 0 +szShow = "" +n = InStr(szReceived, Chr(10)) +Receive = False +Do While n > 0 + If n = 1 Then ' to prevent Mid(... , n - 1, ...) ! + szMessage = "" + Else + szMessage = Left(szReceived, n - IIf(Mid(szReceived, n - 1, 1) = Chr(13), 2, 1)) + End If + If Left(szMessage, 11) = "message_id=" Then + Set_nMessageId = Str2Lng(Mid(szMessage, 12)) + m = InStr(szMessage, "&message=") + If m > 0 Then + szShow = szShow + UrlDecode(Mid(szMessage, m + 9)) + Receive = True + End If + End If + szReceived = Mid(szReceived, n + 1) + n = InStr(szReceived, Chr(10)) +Loop +frmPHPChat.txtRecv.Text = frmPHPChat.txtRecv.Text + szShow +frmPHPChat.txtRecv.SelStart = Len(frmPHPChat.txtRecv.Text) +If Set_bMsgLogs Then + nLogFileNo = FreeFile + On Error GoTo lnErrorOpen + Open App_szPath + "PHPCHATC.LOG" For Append As #nLogFileNo + On Error GoTo 0 + Print #nLogFileNo, szShow + Close #nLogFileNo +End If +App_dfLastTime = Timer + +Exit Function +lnErrorOpen: +On Error GoTo 0 +App_dfLastTime = Timer + +End Function + +Public Sub Logon(ByVal bSettings As Boolean, Optional ByVal frm As Form = Null) + +Dim bResponse As Boolean +bResponse = False +On Error GoTo lnErrorOpen +bResponse = (UCase(frmPHPChat.inet.OpenURL(Set_szServerUrl + "phpchat.php")) = "PHPCHAT") +lnErrorOpen: +On Error GoTo 0 +If bResponse Then + Set_nMessageId = Str2Lng(GetSetting("PHPChat", "Room", Set_szChatRoom + "@" + Set_szServerUrl, "0"), 0) + If Set_bLogon Then + On Error Resume Next + frmPHPChat.inet.OpenURL Set_szServerUrl + "send_message.php?room=" + UrlEncode(Set_szChatRoom) + "&message=" + ParseInfo(Set_szLogon) + On Error GoTo 0 + Sleep 100 + Receive + End If + App_bLogon = True +Else + MsgBox "Server """ + Set_szServerUrl + """ not connected!", vbExclamation + If bSettings Then + frmSettings.Show vbModal, frm + If App_bUpdated Then + Logon False, frm + End If + End If +End If + +End Sub + +Public Sub Send(ByVal sz As String) + +On Error Resume Next +If Set_bSend Then + frmPHPChat.inet.OpenURL Set_szServerUrl + "send_message.php?room=" + UrlEncode(Set_szChatRoom) + "&message=" + ParseInfo(Set_szSend, sz) +Else + frmPHPChat.inet.OpenURL Set_szServerUrl + "send_message.php?room=" + UrlEncode(Set_szChatRoom) + "&message=" + UrlEncode(sz) +End If +On Error GoTo 0 +Sleep 100 +Receive + +End Sub + +Public Sub Logoff() + +If Set_bLogoff Then + On Error Resume Next + frmPHPChat.inet.OpenURL Set_szServerUrl + "send_message.php?room=" + UrlEncode(Set_szChatRoom) + "&message=" + ParseInfo(Set_szLogoff) + On Error GoTo 0 + Sleep 100 + Receive +End If +SaveSetting "PHPChat", "Room", Set_szChatRoom + "@" + Set_szServerUrl, LTrim(Str(Set_nMessageId)) +App_bLogon = False + +End Sub + +Public Function ParseInfo(ByVal sz As String, Optional ByVal szContent As String = "") As String + +Dim szReturn As String +szReturn = Replace(sz, "${room}", Set_szChatRoom, , , vbTextCompare) +szReturn = Replace(szReturn, "${name}", Set_szMyName, , , vbTextCompare) +szReturn = Replace(szReturn, "${date}", Date, , , vbTextCompare) +szReturn = Replace(szReturn, "${time}", Time, , , vbTextCompare) +szReturn = Replace(szReturn, "${return}", vbCrLf, , , vbTextCompare) +szReturn = Replace(szReturn, "${message}", szContent, , , vbTextCompare) +ParseInfo = UrlEncode(szReturn) + +End Function + +Public Sub LoadRegs() + +Set_szServerUrl = GetSetting("PHPChat", "Client", "ServerURL", "http://live.elephantbase.net/") +Set_szChatRoom = GetSetting("PHPChat", "Client", "ChatRoom", "Default Room") +Set_szMyName = GetSetting("PHPChat", "Client", "MyName", "Anonymous") +Set_nRefresh = Str2Lng(GetSetting("PHPChat", "Client", "Refresh", "0"), 0, 2) +Set_bFocus = Str2Lng(GetSetting("PHPChat", "Client", "Focus", "1")) <> 0 +Set_bBeep = Str2Lng(GetSetting("PHPChat", "Client", "Beep", "1")) <> 0 +Set_bLogon = Str2Lng(GetSetting("PHPChat", "Client", "Logon", "1")) <> 0 +Set_bSend = Str2Lng(GetSetting("PHPChat", "Client", "Send", "1")) <> 0 +Set_bLogoff = Str2Lng(GetSetting("PHPChat", "Client", "Logoff", "1")) <> 0 +Set_szLogon = GetSetting("PHPChat", "Client", "LogonInfo", "[${name} Connected at ${time}]${return}") +Set_szSend = GetSetting("PHPChat", "Client", "SendInfo", "[${name} Sent at ${time}]${return}${message}${return}") +Set_szLogoff = GetSetting("PHPChat", "Client", "LogoffInfo", "[${name} Disconnected at ${time}]${return}") +Set_bMsgLogs = Str2Lng(GetSetting("PHPChat", "Client", "MsgLogs", "0")) <> 0 + +End Sub + +Public Sub SaveRegs() + +SaveSetting "PHPChat", "Client", "ServerURL", Set_szServerUrl +SaveSetting "PHPChat", "Client", "ChatRoom", Set_szChatRoom +SaveSetting "PHPChat", "Client", "MyName", Set_szMyName +SaveSetting "PHPChat", "Client", "Refresh", LTrim(Str(Set_nRefresh)) +SaveSetting "PHPChat", "Client", "Focus", IIf(Set_bFocus, "1", "0") +SaveSetting "PHPChat", "Client", "Beep", IIf(Set_bBeep, "1", "0") +SaveSetting "PHPChat", "Client", "Logon", IIf(Set_bLogon, "1", "0") +SaveSetting "PHPChat", "Client", "Send", IIf(Set_bSend, "1", "0") +SaveSetting "PHPChat", "Client", "Logoff", IIf(Set_bLogoff, "1", "0") +SaveSetting "PHPChat", "Client", "LogonInfo", Set_szLogon +SaveSetting "PHPChat", "Client", "SendInfo", Set_szSend +SaveSetting "PHPChat", "Client", "LogoffInfo", Set_szLogoff +SaveSetting "PHPChat", "Client", "MsgLogs", IIf(Set_bMsgLogs, "1", "0") + +End Sub + +Public Sub Main() + +Dim dfThisTime As Double +LoadRegs +App_szPath = App.Path +App_szPath = App_szPath + IIf(Right(App_szPath, 1) = "\", "", "\") +App_bLogon = False +frmPHPChat.Show +frmPHPChat.txtSend.SetFocus +Logon True +If App_bLogon Then + App_dfLastTime = Timer + App_bCtrlEnter = False + Do While App_bLogon + dfThisTime = Timer + dfThisTime = dfThisTime + IIf(dfThisTime < App_dfLastTime, 86400#, 0#) + If dfThisTime > App_dfLastTime + Choose(Set_nRefresh + 1, 10#, 30#, 60#) Then + If Receive Then + If Set_bFocus Then + frmPHPChat.SetFocus + End If + If Set_bBeep Then + MessageBeep MB_ICONINFORMATION + End If + End If + End If + Sleep 1 + DoEvents + Loop +End If +Unload frmPHPChat +SaveRegs + +End Sub diff --git a/MISC/PHPCHAT/PHPCHATC.FRM b/MISC/PHPCHAT/PHPCHATC.FRM new file mode 100644 index 0000000..13f206e --- /dev/null +++ b/MISC/PHPCHAT/PHPCHATC.FRM @@ -0,0 +1,160 @@ +VERSION 5.00 +Object = "{48E59290-9880-11CF-9754-00AA00C00908}#1.0#0"; "MSINET.OCX" +Begin VB.Form frmPHPChat + BorderStyle = 1 'Fixed Single + Caption = "PHPChat Client" + ClientHeight = 3195 + ClientLeft = 45 + ClientTop = 330 + ClientWidth = 4680 + BeginProperty Font + Name = "宋体" + Size = 9 + Charset = 134 + Weight = 400 + Underline = 0 'False + Italic = 0 'False + Strikethrough = 0 'False + EndProperty + MaxButton = 0 'False + ScaleHeight = 3195 + ScaleWidth = 4680 + StartUpPosition = 3 'Windows Default + Begin InetCtlsObjects.Inet inet + Left = 0 + Top = 0 + _ExtentX = 1005 + _ExtentY = 1005 + _Version = 393216 + End + Begin VB.CommandButton btnSettings + Caption = "Settings" + Height = 375 + Left = 2400 + TabIndex = 4 + Top = 2760 + Width = 975 + End + Begin VB.CommandButton btnExit + Cancel = -1 'True + Caption = "Exit" + Height = 375 + Left = 3600 + TabIndex = 5 + Top = 2760 + Width = 855 + End + Begin VB.CommandButton btnSend + Caption = "Send" + Height = 375 + Left = 240 + TabIndex = 2 + Top = 2760 + Width = 855 + End + Begin VB.CommandButton btnClear + Caption = "Clear" + Height = 375 + Left = 1320 + TabIndex = 3 + Top = 2760 + Width = 855 + End + Begin VB.TextBox txtSend + Height = 495 + Left = 120 + MultiLine = -1 'True + ScrollBars = 2 'Vertical + TabIndex = 1 + Top = 2160 + Width = 4455 + End + Begin VB.TextBox txtRecv + Height = 1935 + Left = 120 + Locked = -1 'True + MultiLine = -1 'True + ScrollBars = 2 'Vertical + TabIndex = 0 + Top = 120 + Width = 4455 + End +End +Attribute VB_Name = "frmPHPChat" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False +' PHPChat Client - A NetMeeting Client Program +' Designed by Morning Yellow, Version: 1.0, Last Modified: Sep. 2006 +' Copyright (C) 2004-2006 www.elephantbase.net +' +' This program is free software; you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation; either version 2 of the License, or +' (at your option) any later version. + +' 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. + +Option Explicit + +Private Declare Function lstrlenA Lib "KERNEL32.DLL" (ByVal lpString As Long) As Long + +Private Sub Form_UnLoad(nCancel As Integer) + +Logoff + +End Sub + +Private Sub txtSend_KeyDown(nKeyCode As Integer, nShift As Integer) + +If nKeyCode = vbKeyReturn And (nShift And vbCtrlMask) <> 0 Then + btnSend_Click + txtSend.Locked = True +End If + +End Sub + +Private Sub txtSend_KeyUp(nKeyCode As Integer, nShift As Integer) + +If txtSend.Locked Then + txtSend.Locked = False +End If + +End Sub + +Private Sub btnSend_Click() + +Send txtSend.Text +txtSend.Text = "" + +End Sub + +Private Sub btnClear_Click() + +txtRecv.Text = "" + +End Sub + +Private Sub btnSettings_Click() + +frmSettings.Show vbModal, Me +If App_bUpdated Then + Set_nMessageId = Str2Lng(GetSetting("PHPChat", "Room", Set_szChatRoom + "@" + Set_szServerUrl, "0"), 0) + Logon True, Me +End If + +End Sub + +Private Sub btnExit_Click() + +Unload Me + +End Sub diff --git a/MISC/PHPCHAT/PHPCHATC.VBP b/MISC/PHPCHAT/PHPCHATC.VBP new file mode 100644 index 0000000..b369374 --- /dev/null +++ b/MISC/PHPCHAT/PHPCHATC.VBP @@ -0,0 +1,47 @@ +Type=Exe +Form=PHPCHATC.FRM +Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\WINDOWS\system32\stdole2.tlb#OLE Automation +Module=mdlPHPChat; PHPCHATC.BAS +Form=SETTINGS.FRM +Object={48E59290-9880-11CF-9754-00AA00C00908}#1.0#0; MSINET.OCX +Module=mdlBase; ..\..\utility\BASE.BAS +IconForm="frmPHPChat" +Startup="Sub Main" +HelpFile="" +Title="PHPChat Client" +ExeName32="PHPCHATC.EXE" +Path32="..\..\BIN" +Command32="" +Name="prjPHPChatClient" +HelpContextID="0" +CompatibleMode="0" +MajorVer=1 +MinorVer=0 +RevisionVer=0 +AutoIncrementVer=0 +ServerSupportFiles=0 +VersionComments="PHPChat Client, (C) 2004-2006 www.elephantbase.net" +VersionCompanyName="Author: Morning Yellow (Address: Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)" +VersionFileDescription="PHPChat Client" +VersionLegalCopyright="PHPChat Client, (C) 2004-2006 www.elephantbase.net" +VersionLegalTrademarks="www.elephantbase.net" +VersionProductName="PHPChat Client" +CompilationType=0 +OptimizationType=0 +FavorPentiumPro(tm)=0 +CodeViewDebugInfo=0 +NoAliasing=0 +BoundsCheck=0 +OverflowCheck=0 +FlPointCheck=0 +FDIVCheck=0 +UnroundedFP=0 +StartMode=0 +Unattended=0 +Retained=0 +ThreadPerObject=0 +MaxNumberOfThreads=1 +DebugStartupOption=0 + +[MS Transaction Server] +AutoRefresh=1 diff --git a/MISC/PHPCHAT/SETTINGS.FRM b/MISC/PHPCHAT/SETTINGS.FRM new file mode 100644 index 0000000..8a4a687 --- /dev/null +++ b/MISC/PHPCHAT/SETTINGS.FRM @@ -0,0 +1,309 @@ +VERSION 5.00 +Begin VB.Form frmSettings + BorderStyle = 3 'Fixed Dialog + Caption = "Settings" + ClientHeight = 3195 + ClientLeft = 45 + ClientTop = 330 + ClientWidth = 4680 + BeginProperty Font + Name = "宋体" + Size = 9 + Charset = 0 + Weight = 400 + Underline = 0 'False + Italic = 0 'False + Strikethrough = 0 'False + EndProperty + Icon = "SETTINGS.frx":0000 + MaxButton = 0 'False + MinButton = 0 'False + ScaleHeight = 3195 + ScaleWidth = 4680 + ShowInTaskbar = 0 'False + StartUpPosition = 3 'Windows Default + Begin VB.CommandButton btnReset + Caption = "Reset" + Height = 375 + Left = 360 + TabIndex = 19 + Top = 2760 + Width = 1095 + End + Begin VB.TextBox txtLogoff + Height = 285 + Left = 1080 + TabIndex = 15 + Top = 1920 + Width = 3495 + End + Begin VB.CheckBox chkSend + Caption = "Send" + Height = 255 + Left = 120 + TabIndex = 12 + Top = 1560 + Width = 855 + End + Begin VB.TextBox txtSend + Height = 285 + Left = 1080 + TabIndex = 13 + Top = 1560 + Width = 3495 + End + Begin VB.TextBox txtLogon + Height = 285 + Left = 1080 + TabIndex = 11 + Top = 1200 + Width = 3495 + End + Begin VB.CheckBox chkLogoff + Caption = "Logoff" + Height = 255 + Left = 120 + TabIndex = 14 + Top = 1920 + Width = 855 + End + Begin VB.CheckBox chkLogon + Caption = "Logon" + Height = 255 + Left = 120 + TabIndex = 10 + Top = 1200 + Width = 855 + End + Begin VB.CheckBox chkBeep + Caption = "Beep" + Height = 300 + Left = 3720 + TabIndex = 9 + Top = 840 + Width = 855 + End + Begin VB.CommandButton btnClearLog + Caption = "Clear Log" + Height = 375 + Left = 3240 + TabIndex = 18 + Top = 2280 + Width = 1095 + End + Begin VB.TextBox txtMyName + Height = 285 + Left = 1080 + TabIndex = 7 + Top = 840 + Width = 1695 + End + Begin VB.TextBox txtChatRoom + Height = 285 + Left = 1080 + TabIndex = 3 + Top = 480 + Width = 1695 + End + Begin VB.TextBox txtServerUrl + Height = 285 + Left = 1080 + TabIndex = 1 + Top = 120 + Width = 3495 + End + Begin VB.CheckBox chkFocus + Caption = "Focus" + Height = 300 + Left = 2880 + TabIndex = 8 + Top = 840 + Width = 855 + End + Begin VB.ComboBox optRefresh + Height = 300 + ItemData = "SETTINGS.frx":000C + Left = 3600 + List = "SETTINGS.frx":000E + Style = 2 'Dropdown List + TabIndex = 5 + Top = 480 + Width = 975 + End + Begin VB.CheckBox chkMsgLogs + Caption = "Message Logs" + Height = 255 + Left = 120 + TabIndex = 16 + Top = 2280 + Width = 1455 + End + Begin VB.CommandButton btnViewLog + Caption = "View Log" + Height = 375 + Left = 1800 + TabIndex = 17 + Top = 2280 + Width = 1095 + End + Begin VB.CommandButton btnCancel + Cancel = -1 'True + Caption = "Cancel" + Height = 375 + Left = 3240 + TabIndex = 21 + Top = 2760 + Width = 1095 + End + Begin VB.CommandButton btnConfirm + Caption = "OK" + Height = 375 + Left = 1800 + TabIndex = 20 + Top = 2760 + Width = 1095 + End + Begin VB.Label lblMyName + Alignment = 1 'Right Justify + Caption = "My Name" + Height = 255 + Left = 240 + TabIndex = 6 + Top = 840 + Width = 735 + End + Begin VB.Label lblChatRoom + Alignment = 1 'Right Justify + Caption = "Chat Room" + Height = 255 + Left = 120 + TabIndex = 2 + Top = 480 + Width = 855 + End + Begin VB.Label lblServerUrl + Alignment = 1 'Right Justify + Caption = "Server URL" + Height = 255 + Left = 0 + TabIndex = 0 + Top = 120 + Width = 975 + End + Begin VB.Label lblRefresh + Caption = "Refresh" + Height = 255 + Left = 2880 + TabIndex = 4 + Top = 480 + Width = 735 + End +End +Attribute VB_Name = "frmSettings" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False +' PHPChat Client - A NetMeeting Client Program +' Designed by Morning Yellow, Version: 1.0, Last Modified: Sep. 2006 +' Copyright (C) 2004-2006 www.elephantbase.net +' +' This program is free software; you can redistribute it and/or +' modify it under the terms of the GNU General Public License +' as published by the Free Software Foundation; either version 2 +' of the License, or (at your option) any later version. +' +' 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Option Explicit + +Private Const SW_SHOWNORMAL As Long = 1 +Private Declare Sub ShellExecuteA Lib "SHELL32.DLL" (ByVal hWnd As Long, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) + +Private Sub Form_Load() + +optRefresh.Clear +optRefresh.AddItem "10 Sec", 0 +optRefresh.AddItem "30 Sec", 1 +optRefresh.AddItem "60 Sec", 2 + +txtServerUrl.Text = Set_szServerUrl +txtChatRoom.Text = Set_szChatRoom +txtMyName.Text = Set_szMyName +optRefresh.ListIndex = Set_nRefresh +chkFocus.Value = IIf(Set_bFocus, 1, 0) +chkBeep.Value = IIf(Set_bBeep, 1, 0) +chkLogon.Value = IIf(Set_bLogon, 1, 0) +chkSend.Value = IIf(Set_bSend, 1, 0) +chkLogoff.Value = IIf(Set_bLogoff, 1, 0) +chkMsgLogs.Value = IIf(Set_bMsgLogs, 1, 0) +txtLogon.Text = Set_szLogon +txtSend.Text = Set_szSend +txtLogoff.Text = Set_szLogoff + +End Sub + +Private Sub btnViewLog_Click() + +ShellExecuteA 0, vbNullString, App_szPath + "PHPCHATC.LOG", vbNullString, vbNullString, SW_SHOWNORMAL + +End Sub + +Private Sub btnClearLog_Click() + +Dim nLogFileNo As Integer +nLogFileNo = FreeFile +On Error Resume Next +Open App_szPath + "PHPCHATC.LOG" For Output As #nLogFileNo +Close #nLogFileNo +On Error GoTo 0 + +End Sub + +Private Sub btnReset_Click() + +Set_nMessageId = 0 +SaveSetting "PHPChat", "Room", Set_szChatRoom + "@" + Set_szServerUrl, LTrim(Str(Set_nMessageId)) + +End Sub + +Private Sub btnConfirm_Click() + +If txtServerUrl.Text = Set_szServerUrl And txtChatRoom.Text = Set_szChatRoom Then + App_bUpdated = False +Else + App_bUpdated = True + If App_bLogon Then + Logoff + End If +End If +Set_szServerUrl = txtServerUrl.Text + IIf(Right(txtServerUrl.Text, 1) = "/", "", "/") +Set_szChatRoom = txtChatRoom.Text +Set_szMyName = txtMyName.Text +Set_nRefresh = optRefresh.ListIndex +Set_bFocus = (chkFocus.Value = 1) +Set_bBeep = (chkBeep.Value = 1) +Set_bLogon = (chkLogon.Value = 1) +Set_bSend = (chkSend.Value = 1) +Set_bLogoff = (chkLogoff.Value = 1) +Set_bMsgLogs = (chkMsgLogs.Value = 1) +Set_szLogon = txtLogon.Text +Set_szSend = txtSend.Text +Set_szLogoff = txtLogoff.Text +Unload Me + +End Sub + +Private Sub btnCancel_Click() + +App_bUpdated = False +Unload Me + +End Sub diff --git a/MISC/PHPCHAT/SETTINGS.FRX b/MISC/PHPCHAT/SETTINGS.FRX new file mode 100644 index 0000000..1c5a625 Binary files /dev/null and b/MISC/PHPCHAT/SETTINGS.FRX differ diff --git a/MISC/PHPCHAT/config.php b/MISC/PHPCHAT/config.php new file mode 100644 index 0000000..4c130a4 --- /dev/null +++ b/MISC/PHPCHAT/config.php @@ -0,0 +1,7 @@ + diff --git a/MISC/PHPCHAT/deploy.php b/MISC/PHPCHAT/deploy.php new file mode 100644 index 0000000..e697654 --- /dev/null +++ b/MISC/PHPCHAT/deploy.php @@ -0,0 +1,10 @@ + diff --git a/MISC/PHPCHAT/list_message.php b/MISC/PHPCHAT/list_message.php new file mode 100644 index 0000000..c9e230e --- /dev/null +++ b/MISC/PHPCHAT/list_message.php @@ -0,0 +1,16 @@ + ' . $message_id); + while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) { + echo('message_id=' . $line['message_id'] . '&message=' . urlencode($line['message']) . "\n"); + } + } + mysql_close(); +?> diff --git a/MISC/PHPCHAT/phpchat.php b/MISC/PHPCHAT/phpchat.php new file mode 100644 index 0000000..2624d21 --- /dev/null +++ b/MISC/PHPCHAT/phpchat.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/MISC/PHPCHAT/send_message.php b/MISC/PHPCHAT/send_message.php new file mode 100644 index 0000000..09893c4 --- /dev/null +++ b/MISC/PHPCHAT/send_message.php @@ -0,0 +1,19 @@ + diff --git a/MISC/SHOWSIZE/showsize.c b/MISC/SHOWSIZE/showsize.c new file mode 100644 index 0000000..fe676ae --- /dev/null +++ b/MISC/SHOWSIZE/showsize.c @@ -0,0 +1,20 @@ +#include + +int main(int argc, char **argv) { + FILE *fp; + if (argc < 3) { + printf("=== File Size Printing Program ===\n"); + printf("Usage: SHOWSIZE File Format-with-\"%%d\"\n"); + return 0; + } + fp = fopen(argv[1], "rb"); + if (fp == NULL) { + printf("Unable to Open File: %s\n", argv[1]); + return 0; + } + fseek(fp, 0, SEEK_END); + printf(argv[2], ftell(fp)); + printf("\n"); + fclose(fp); + return 0; +} diff --git a/MISC/TEST/BGROUND.GIF b/MISC/TEST/BGROUND.GIF new file mode 100644 index 0000000..b981179 Binary files /dev/null and b/MISC/TEST/BGROUND.GIF differ diff --git a/MISC/TEST/BIGINT32.CPP b/MISC/TEST/BIGINT32.CPP new file mode 100644 index 0000000..8aed0a0 --- /dev/null +++ b/MISC/TEST/BIGINT32.CPP @@ -0,0 +1,152 @@ +#include +#include "../../base/base.h" + +#ifdef _MSC_VER + +#pragma warning(disable: 4035) + +uint32_t BigIntAdd(uint32_t *lpDst, const uint32_t *lpSrc1, const uint32_t *lpSrc2, uint32_t nLen) { + __asm { + mov ecx, nLen; + or ecx, ecx; + jz f0; + mov esi, lpSrc1; + mov ebx, lpSrc2; + mov edi, lpDst; + r0: + mov eax, [esi]; + lea esi, [esi + 4]; + adc eax, [ebx]; + lea ebx, [ebx + 4]; + mov [edi], eax; + lea edi, [edi + 4]; + loop r0; + f0: + mov eax, 0; + adc eax, 0; + } +} + +uint32_t BigIntSub(uint32_t *lpDst, const uint32_t *lpSrc1, const uint32_t *lpSrc2, uint32_t nLen) { + __asm { + mov ecx, nLen; + or ecx, ecx; + jz f0; + mov edi, lpDst; + mov esi, lpSrc1; + mov ebx, lpSrc2; + r0: + mov eax, [esi]; + lea esi, [esi + 4]; + sbb eax, [ebx]; + lea ebx, [ebx + 4]; + mov [edi], eax; + lea edi, [edi + 4]; + loop r0; + f0: + mov eax, 0; + sbb eax, 0; + } +} + +uint32_t BigIntInc(uint32_t *lp, uint32_t nLen) { + /* uint32_t cf = 1; + * for (uint32_t i = 0; i < nLen; i ++) { + * cf:lp[i] += cf; + * if (cf != 0) { + * break; + * } + * } + * return cf; + */ + __asm { + mov ecx, nLen; + or ecx, ecx; + jz f0; + mov ebx, lp; + stc; + r0: + mov eax, [ebx]; + adc eax, 0; + mov [ebx], eax; + lea ebx, [ebx + 4]; + jnc f0; + loop r0; + f0: + mov eax, 0; + adc eax, 0; + } +} + +uint32_t BigIntDec(uint32_t *lp, uint32_t nLen) { + /* uint32_t cf = -1; + * for (uint32_t i = 0; i < nLen; i ++) { + * cf:lp[i] -= cf; + * if (cf != 0) { + * break; + * } + * } + * return cf; + */ + __asm { + mov ecx, nLen; + or ecx, ecx; + jz f0; + mov ebx, lp; + stc; + r0: + mov eax, [ebx]; + sbb eax, 0; + mov [ebx], eax; + lea ebx, [ebx + 4]; + jnc f0; + loop r0; + f0: + mov eax, 0; + sbb eax, 0; + } +} + +#pragma warning(default: 4035) + +void BigIntMul(uint32_t *lpDst, const uint32_t *lpSrc1, const uint32_t *lpSrc2, uint32_t nLen1, uint32_t nLen2) { + uint32_t i, iSrc1; + memset(lpDst, 0, (nLen1 + nLen2) * sizeof(uint32_t)); + for (i = 0; i < nLen1; i ++) { + iSrc1 = lpSrc1[i]; + /* for (uint32_t j = 0; j < nLen2; j ++) { + * uint32_t ebx = 0; + * edx:eax = lpSrc[i] * lpSrc[j] + lpDst[i + j] + ebx; + * lpDst[i + j] = eax; + * ebx = edx; + * } + * lpDst[i + j] = ebx; + */ + __asm { + mov ecx, nLen2; + or ecx, ecx; + jz f0; + mov esi, lpSrc2; + mov edi, i; + lea edi, [edi * 4]; + add edi, lpDst; + xor ebx, ebx; + r0: + mov eax, [esi]; + lea esi, [esi + 4]; + mul iSrc1; + add eax, [edi]; + adc edx, 0; + add eax, ebx; + adc edx, 0; + mov [edi], eax; + lea edi, [edi + 4]; + mov ebx, edx; + loop r0; + f0: + mov [edi], ebx; + } + } +} + +#endif diff --git a/MISC/TEST/BIGINT32.H b/MISC/TEST/BIGINT32.H new file mode 100644 index 0000000..14ceebd --- /dev/null +++ b/MISC/TEST/BIGINT32.H @@ -0,0 +1,12 @@ +#include "../../base/base.h" + +#ifndef BIGINT32_H +#define BIGINT32_H + +uint32_t BigIntAdd(uint32_t *lpDst, const uint32_t *lpSrc1, const uint32_t *lpSrc2, uint32_t nLen); +uint32_t BigIntSub(uint32_t *lpDst, const uint32_t *lpSrc1, const uint32_t *lpSrc2, uint32_t nLen); +uint32_t BigIntInc(uint32_t *lp, uint32_t nLen); +uint32_t BigIntDec(uint32_t *lp, uint32_t nLen); +void BigIntMul(uint32_t *lpDst, const uint32_t *lpSrc1, const uint32_t *lpSrc2, uint32_t nLen1, uint32_t nLen2); + +#endif diff --git a/MISC/TEST/MAKEFILE.BAT b/MISC/TEST/MAKEFILE.BAT new file mode 100644 index 0000000..09b650d --- /dev/null +++ b/MISC/TEST/MAKEFILE.BAT @@ -0,0 +1,6 @@ +@ECHO OFF +CL /O2 /W3 /Fe..\..\BIN\GENPRIME.EXE GENPRIME.CPP +CL /O2 /W3 /Fe..\..\BIN\NETWORK.EXE MATRIX.CPP NETWORK.CPP +CL /O2 /W3 /Fe..\..\BIN\NETWORK2.EXE MATRIX.CPP NETWORK2.CPP +CL /O2 /W3 /Fe..\..\BIN\FFTMUL.EXE BIGINT32.CPP FFT.CPP FFTMUL.CPP +DEL *.OBJ diff --git a/MISC/TEST/NETWORK.GIF b/MISC/TEST/NETWORK.GIF new file mode 100644 index 0000000..0ab62b5 Binary files /dev/null and b/MISC/TEST/NETWORK.GIF differ diff --git a/MISC/TEST/NETWORK.HTM b/MISC/TEST/NETWORK.HTM new file mode 100644 index 0000000..cdece75 --- /dev/null +++ b/MISC/TEST/NETWORK.HTM @@ -0,0 +1,90 @@ + + + + + +处理器符点运算能力测试程序(NETWORK.CPP)说明 + + + + +
    +
    +
    电阻网络计算程序说明
    +
    +
     
    +
    +
    黄晨 * 20064
    +
    +
    ( * 上海计算机博弈研究所,eMailwebmaster@elephantbase.net)
    +
    +
     
    +
    +
    +
    +
     
    +
    +
      这是一个用矩阵计算电阻网络表观电阻值的程序,具体的原理可参阅《电阻网络的简化方法》一文。我写的这个程序只涉及到矩阵最简单最基本的算法——高斯列主元消除法,而效率更高的算法应当使用稀疏矩阵,由于能力所限我没有去做。尽管这个程序在算法上有很大的缺陷,但用来测试处理器的符点运算能力是足够了,因为矩阵运算中会涉及到大量加减乘除的符点运算。
    +
      至于程序的由来,还必须从一类高中物理竞赛题目说起,如上图所示的二维无限电阻网络,每一小段(接点之间的线路,如FG)的电阻是1W,求相邻两接点间(FG)的表观电阻。这个问题并不简单,由于FG不仅仅由一段1W的电阻相连,电流还可以走其他的路径,如F-B-C-G,所以表观电阻肯定小于1W。那么这道题到底如何解呢?
    +
      可以假设对F点输入1A的恒定电流,在G点输出,求得FG两点间的电压就能算出表观电阻。这个模型可以分解成两部分,一是电流从F点流入,在网络的无穷远处流出。由于网络的对称性,电流在F点会平均分解成4个支路FEFBFGFJ,支路上的电流都是0.25A。再考察电流从网络的无穷远处流入,在G点流出的情况,同样在FG支路上的电流也是0.25A(和前一种情况一样,电流从F流向G)。把这两种情况叠加,就构成了现在电流从F流入、从G流出的情况,此时每个支路上的电流也将是两种情况对应支路上的叠加,因此FG支路上的电流是0.5A。这样就很容易得到,FG两点的电压是0.5V,因此FG两点在整个网络中的表观电阻是0.5W
    +
      尽管我自认为高中物理已经滚瓜烂熟了,但是进入大学后我收到一位高中同学的求教,看似类似的题目却让我不知所措。题目类型是和上题一模一样的电阻网络,而所求的却不是接点FG间的表观电阻,而是在接点FK间的!事实上这道崭新的题目根本无法用原来的思路来解,如果当我们仍旧固执地考虑,向F点输入1A的恒定电流并在无穷远处输出,电流在F点平均分成4个支路,那么在G点却遇到了麻烦,即便GCGK具有对称性,我们也不知道电流是多少,因为GH上的电流也是未知的。
    +
      六七年过去了,尽管我还是没有解开这个谜团,但是这类问题已经不再捆绕我了,当严格的求解无法得到精确答案时,我们至少可以通过数值求解的办法得到相对准确的结果。在这个程序中,我构造了一个2nx2n的电阻网络,借助矩阵求对角的两点间的表观电阻,当n充分大时,求得的电阻值就会非常接近无限网络的结果。
    +
    + + diff --git a/MISC/TEST/complex.h b/MISC/TEST/complex.h new file mode 100644 index 0000000..c14b35d --- /dev/null +++ b/MISC/TEST/complex.h @@ -0,0 +1,361 @@ +#include + +#ifndef COMPLEX_H +#define COMPLEX_H + +struct complex { + double re, im; + + complex(void) { + } + + complex(double opr1, double opr2) { + re = opr1; + im = opr2; + } + + complex conj(void) const { + return complex(re, -im); + } + + double norm(void) const { + return re * re + im * im; + } + + int operator ==(complex opr) const { + return re == opr.re && im == opr.im; + } + + int operator !=(complex opr) const { + return re == opr.re && im == opr.im; + } + + complex operator +(void) const { + return *this; + } + + complex operator -(void) const { + return complex(-re, -im); + } + + complex &operator ++(void) { + re += 1.0; + return *this; + } + + complex &operator --(void) { + re -= 1.0; + return *this; + } + + complex mul_i() const { + return complex(-im, re); + } + + complex div_i() const { + return complex(im, -re); + } + + complex operator +(double opr) const { + return complex(re + opr, im); + } + + complex operator +(complex opr) const { + return complex(re + opr.re, im + opr.im); + } + + complex operator -(double opr) const { + return complex(re - opr, im); + } + + complex operator -(complex opr) const { + return complex(re - opr.re, im - opr.im); + } + + complex operator *(double opr2) const { + return complex(re * opr2, im * opr2); + } + + complex operator *(complex opr) const { + return complex(re * opr.re - im * opr.im, re * opr.im + im * opr.re); + } + + complex operator /(double opr) const { + return complex(re / opr, im / opr); + } + + complex operator /(complex opr) const { + return *this * opr.conj() / opr.norm(); + } + + complex &operator =(double opr) { + re = opr; + im = 0; + return *this; + } + + complex &operator =(complex opr) { + re = opr.re; + im = opr.im; + return *this; + } + + complex &operator +=(double opr) { + re += opr; + return *this; + } + + complex &operator +=(complex opr) { + re += opr.re; + im += opr.im; + return *this; + } + + complex &operator -=(double opr) { + re -= opr; + return *this; + } + + complex &operator -=(complex opr) { + re -= opr.re; + im -= opr.im; + return *this; + } + + complex &operator *=(double opr) { + re *= opr; + im *= opr; + return *this; + } + + complex &operator *=(complex opr) { + *this = *this * opr; + return *this; + } + + complex &operator /=(double opr) { + re /= opr; + im /= opr; + return *this; + } + + complex &operator /=(complex opr) { + *this = *this / opr; + return *this; + } +}; + +inline complex operator +(double opr1, complex opr2) { + return complex(opr1 + opr2.re, opr2.im); +} + +inline complex operator -(double opr1, complex opr2) { + return complex(opr1 - opr2.re, -opr2.im); +} + +inline complex operator *(double opr1, complex opr2) { + return complex(opr1 * opr2.re, opr1 * opr2.im); +} + +inline complex operator /(double opr1, complex opr2) { + return opr1 * opr2.conj() / opr2.norm(); +} + +inline double real(complex opr) { + return opr.re; +} + +inline double imag(complex opr) { + return opr.im; +} + +inline complex conj(complex opr) { + return opr.conj(); +} + +inline double norm(complex opr) { + return opr.norm(); +} + +inline double arg(complex opr) { + return atan2(opr.im, opr.re); +} + +inline double abs(complex opr) { + return sqrt(norm(opr)); +} + +#ifdef _MSC_VER + +__forceinline complex cos_isin(double opr) { + complex t; + __asm { + fld opr; + fsincos; + fstp t.re; + fstp t.im; + } + return t; +} + +#else + +static __inline__ complex cos_isin(double opr) { + double st0, st1; + asm __volatile__ ( + "fsincos" "\n\t" + : "=t" (st0), "=u" (st1) + : "0" (opr) + ); + return complex(st0, st1); +} + +#endif + +inline complex cosh_isinh(double opr) { + double t1, t2; + t1 = exp(opr); + t2 = 0.5 * t1 - 0.5 / t1; + return complex(t1 - t2, t2); +} + +inline complex polar(double opr1, double opr2) { + return opr1 * cos_isin(opr2); +} + +inline complex sqrt(complex opr) { + double t; + if (opr.re > 0.0) { + t = sqrt((abs(opr) + opr.re) * 0.5); + return complex(t, opr.im * 0.5 / t); + } else { + t = sqrt((abs(opr) - opr.re) * 0.5); + if (opr.im > 0.0) { + return complex(opr.im * 0.5 / t, t); + } else if (opr.im == 0.0) { + return complex(0.0, -t); + } else { + return complex(-opr.im * 0.5 / t, -t); + } + } +} + +inline complex exp(complex opr) { + return exp(opr.re) * cos_isin(opr.im); +} + +inline complex log(complex opr) { + return complex(log(abs(opr)), arg(opr)); +} + +inline complex pow(complex opr1, double opr2) { + return exp(opr2 * log(opr1)); +} + +inline complex pow(double opr1, complex opr2) { + return exp(opr2 * log(opr1)); +} + +inline complex pow(complex opr1, complex opr2) { + return exp(opr2 * log(opr1)); +} + +inline complex sin(complex opr) { + complex t1, t2; + t1 = cos_isin(opr.re); + t2 = cosh_isinh(opr.im); + return complex(t1.im * t2.re, t1.re * t2.im); +} + +inline complex cos(complex opr) { + complex t1, t2; + t1 = cos_isin(opr.re); + t2 = cosh_isinh(opr.im); + return complex(t1.re * t2.re, -t1.im * t2.im); +} + +inline complex tan(complex opr) { + double t1, t2; + t1 = tan(opr.re); + t2 = tanh(opr.im); + return complex(t1, t2) / complex(1.0, -t1 * t2); +} + +inline complex sinh(complex opr) { + complex t1, t2; + t1 = cosh_isinh(opr.re); + t2 = cos_isin(opr.im); + return complex(t1.im * t2.re, t1.re * t2.im); +} + +inline complex cosh(complex opr) { + complex t1, t2; + t1 = cosh_isinh(opr.re); + t2 = cos_isin(opr.im); + return complex(t1.re * t2.re, t1.im * t2.im); +} + +inline complex tanh(complex opr) { + double t1, t2; + t1 = tanh(opr.re); + t2 = tan(opr.im); + return complex(t1, t2) / complex(1.0, t1 * t2); +} + +#ifdef _MSC_VER + +inline double asinh(double opr) { + if (opr >= 0.0) { + return log(sqrt(opr * opr + 1.0) + opr); + } else { + return -log(sqrt(opr * opr + 1.0) - opr); + } +} + +inline double acosh(double opr) { + return log(opr + sqrt(opr * opr - 1.0)); +} + +inline double atanh(double opr) { + return log((1.0 + opr) / (1.0 - opr)) / 2; +} + +#endif + +inline complex asinh(complex opr) { + complex t; + t = sqrt(opr * opr + 1.0); + if (opr.re * t.re + opr.im * t.im >= 0.0) { + return log(t + opr); + } else { + return -log(t - opr); + } +} + +inline complex acosh(complex opr) { + complex t; + t = sqrt(opr * opr - 1.0); + if (opr.re * t.re + opr.im * t.im >= 0.0) { + return log(opr + t); + } else { + return -log(opr - t); + } +} + +inline complex atanh(complex opr) { + return log((1.0 + opr) / (1.0 - opr)) / 2; +} + +inline complex asin(complex opr) { + return asinh(opr.mul_i()).div_i(); +} + +inline complex acos(complex opr) { + return acosh(opr).mul_i(); +} + +inline complex atan(complex opr) { + return atanh(opr.mul_i()).div_i(); +} + +#endif diff --git a/MISC/TEST/fft.cpp b/MISC/TEST/fft.cpp new file mode 100644 index 0000000..0f4af62 --- /dev/null +++ b/MISC/TEST/fft.cpp @@ -0,0 +1,108 @@ +#include +#include "complex.h" +#include "fft.h" + +static const double PI = 3.141592653589793; + +void MakExp(complex *lpExp, int nLen) { + int i, iMax, jMax; + double t; + complex z; + lpExp[0] = complex(1.0, 0.0); + iMax = 1; + if (nLen < 0) { + jMax = -(nLen / 4); + t = PI / 2; + } else { + jMax = nLen / 4; + t = -PI / 2; + } + while (jMax != 0) { + z = cos_isin(t); + for (i = 0; i < iMax; i ++) { + lpExp[(i * 2 + 1) * jMax] = lpExp[i * 2 * jMax] * z; + } + iMax *= 2; + jMax /= 2; + t /= 2; + } +} + +// base-4 Cooley-Tukey FFT algorithm +void Fft(complex *lpDst, const complex *lpSrc, const complex *lpExp, int nLen) { + int i, j, k, l; + complex a, b, c, d, z; + if (nLen == 1) { + lpDst[0] = lpSrc[0]; + } else if (nLen == 2) { + lpDst[0] = lpSrc[0] + lpSrc[1]; + lpDst[1] = lpSrc[0] - lpSrc[1]; + } else { + j = 0; + for (i = 0; i < nLen; i ++) { + lpDst[i] = lpSrc[j]; + k = nLen >> 1; + while (j >= k && k > 0) { + j -= k; + k >>= 1; + } + j += k; + } + for (i = 0; i < nLen; i += 4) { + a = lpDst[i] + lpDst[i + 1]; + b = lpDst[i + 2] + lpDst[i + 3]; + c = lpDst[i] - lpDst[i + 1]; + d = (lpDst[i + 2] - lpDst[i + 3]) * lpExp[nLen / 4]; + lpDst[i] = a + b; + lpDst[i + 1] = c + d; + lpDst[i + 2] = a - b; + lpDst[i + 3] = c - d; + } + for (i = 4; i < nLen; i <<= 1) { + l = nLen / (i << 1); + for (j = 0; j < nLen; j += i << 1) { + for (k = 0; k < i; k ++) { + z = lpDst[i + j + k] * lpExp[k * l]; + lpDst[i + j + k] = lpDst[j + k] - z; + lpDst[j + k] += z; + } + } + } + } +} + +void RealFft::Exec(complex *lpDst, const double *lpSrc) { + int i, nLen2; + complex z; + if (nLen == 1) { + lpDst[0] = complex(lpSrc[0], 0); + } else { + nLen2 = nLen / 2; + for (i = 0; i < nLen2; i ++) { + lpDst[i] = complex(lpSrc[i * 2], lpSrc[i * 2 + 1]); + } + Fft(lpTemp, lpDst, lpExp2, nLen2); + lpDst[0] = complex((lpTemp[0].re + lpTemp[0].im) * 2, 0); + lpDst[nLen2] = complex((lpTemp[0].re - lpTemp[0].im) * 2, 0); + for (i = 1; i < nLen2; i ++) { + z = lpTemp[nLen2 - i].conj(); + lpDst[i] = lpTemp[i] + z + (lpTemp[i] - z) * lpExp[i].div_i(); + lpDst[nLen - i] = lpDst[i].conj(); + } + for (i = 0; i < nLen; i ++) { + lpDst[i] *= 0.5; + } + } +} + +void InvRealFft::Exec(double *lpDst, const complex *lpSrc) { + int i, nLen; + nLen = rf.nLen; + for (i = 0; i < nLen; i ++) { + lpDst[i] = lpSrc[i].re + lpSrc[i].im; + } + rf.Exec(lpTemp2, lpDst); + for (i = 0; i < nLen; i ++) { + lpDst[i] = (lpTemp2[i].re - lpTemp2[i].im) / nLen; + } +} diff --git a/MISC/TEST/fft.h b/MISC/TEST/fft.h new file mode 100644 index 0000000..1d5e993 --- /dev/null +++ b/MISC/TEST/fft.h @@ -0,0 +1,63 @@ +#include "complex.h" + +#ifndef FFT_H +#define FFT_H + +void MakExp(complex *lpExp, int nLen); +void Fft(complex *lpDst, const complex *lpSrc, const complex *lpExp, int nLen); + +struct RealFft { + int nLen; + complex *lpTemp, *lpExp, *lpExp2; + + RealFft(void) { + // Do Nothing + } + + RealFft(int nLenParam) { + Init(nLenParam); + } + + ~RealFft(void) { + delete[] lpTemp; + delete[] lpExp; + delete[] lpExp2; + } + + void Init(int nLenParam) { + nLen = (nLenParam < 0 ? -nLenParam : nLenParam); + lpTemp = new complex[nLen]; + lpExp = new complex[nLen / 2]; + lpExp2 = new complex[nLen / 4]; + MakExp(lpExp, nLenParam); + MakExp(lpExp2, nLenParam / 2); + } + + void Exec(complex *lpDst, const double *lpSrc); +}; + +struct InvRealFft { + RealFft rf; + complex *lpTemp2; + + InvRealFft(void) { + // Do Nothing + } + + InvRealFft(int nLenParam) { + Init(nLenParam); + } + + ~InvRealFft(void) { + delete[] lpTemp2; + } + + void Init(int nLenParam) { + rf.Init(-nLenParam); + lpTemp2 = new complex[rf.nLen]; + } + + void Exec(double *lpDst, const complex *lpSrc); +}; + +#endif diff --git a/MISC/TEST/fftmul.cpp b/MISC/TEST/fftmul.cpp new file mode 100644 index 0000000..4d3a4d6 --- /dev/null +++ b/MISC/TEST/fftmul.cpp @@ -0,0 +1,123 @@ +#include +#include +#include "../../base/base.h" +#include "../../base/rc4prng.h" +#include "bigint32.h" +#include "complex.h" +#include "fft.h" + +struct FftBigIntMul { + RealFft rf; + InvRealFft irf; + double *dfDst, *dfSrc1, *dfSrc2; + complex *cDst, *cSrc1, *cSrc2; + + FftBigIntMul(void) { + // Do Nothing + } + + FftBigIntMul(int nLenParam) { + Init(nLenParam); + } + + ~FftBigIntMul(void) { + delete[] dfDst; + delete[] dfSrc1; + delete[] dfSrc2; + delete[] cDst; + delete[] cSrc1; + delete[] cSrc2; + } + + void Init(int nLenParam) { + int nLen = nLenParam * 4; + rf.Init(nLen); + irf.Init(nLen); + dfDst = new double[nLen]; + dfSrc1 = new double[nLen]; + dfSrc2 = new double[nLen]; + cDst = new complex[nLen]; + cSrc1 = new complex[nLen]; + cSrc2 = new complex[nLen]; + } + + void Exec(uint32_t *lpDst, const uint32_t *lpSrc1, const uint32_t *lpSrc2); +}; + +void FftBigIntMul::Exec(uint32_t *lpDst, const uint32_t *lpSrc1, const uint32_t *lpSrc2) { + int i, dw, nLen, nLen2; + uint16_t *lpwDst; + const uint16_t *lpcwSrc1, *lpcwSrc2; + + nLen = rf.nLen; + nLen2 = nLen / 2; + lpwDst = (uint16_t *) lpDst; + lpcwSrc1 = (const uint16_t *) lpSrc1; + lpcwSrc2 = (const uint16_t *) lpSrc2; + for (i = 0; i < nLen2; i ++) { + dfSrc1[i] = lpcwSrc1[i]; + dfSrc1[nLen2 + i] = 0.0; + dfSrc2[i] = lpcwSrc2[i]; + dfSrc2[nLen2 + i] = 0.0; + } + rf.Exec(cSrc1, dfSrc1); + rf.Exec(cSrc2, dfSrc2); + for (i = 0; i < nLen; i ++) { + cDst[i] = cSrc1[i] * cSrc2[i]; + } + irf.Exec(dfDst, cDst); + dw = 0; + for (i = 0; i < nLen; i ++) { + dfDst[i] += dw; + dw = (uint32_t) ((dfDst[i] + 0.5) / 65536.0); + lpwDst[i] = (uint16_t) (dfDst[i] - 65536.0 * dw + 0.5); + } +} + +const int TEST_TIMES = 256; + +int main(void) { + RC4Struct rc4; + int64_t llTime; + int i, nArrLen; + uint32_t *lpDst1, *lpDst2, *lpSrc1, *lpSrc2; + + rc4.InitZero(); + nArrLen = 32; + printf("Multiplication Test (in microseconds)\n"); + printf("\n"); + printf(" Bits Normal FFT\n"); + printf("==================\n"); + while (nArrLen <= 2048) { + lpDst1 = new uint32_t[nArrLen * 2]; + lpDst2 = new uint32_t[nArrLen * 2]; + lpSrc1 = new uint32_t[nArrLen]; + lpSrc2 = new uint32_t[nArrLen]; + FftBigIntMul fbim(nArrLen); + for (i = 0; i < nArrLen; i ++) { + lpSrc1[i] = rc4.NextLong(); + lpSrc2[i] = rc4.NextLong(); + } + llTime = GetTime(); + for (i = 0; i < TEST_TIMES; i ++) { + BigIntMul(lpDst1, lpSrc1, lpSrc2, nArrLen, nArrLen); + } + printf("%5d %5d", nArrLen * 32, (int) (GetTime() - llTime) * 1000 / TEST_TIMES); + llTime = GetTime(); + for (i = 0; i < TEST_TIMES; i ++) { + fbim.Exec(lpDst2, lpSrc1, lpSrc2); + } + printf(" %5d\n", (int) (GetTime() - llTime) * 1000 / TEST_TIMES); + for (i = 0; i < nArrLen * 2; i ++) { + if (lpDst1[i] != lpDst2[i]) { + printf("Error at %d: %08X %08X\n", i, lpDst1[i], lpDst2[i]); + } + } + delete[] lpDst1; + delete[] lpDst2; + delete[] lpSrc1; + delete[] lpSrc2; + nArrLen <<= 1; + } + return 0; +}; diff --git a/MISC/TEST/fraction.cpp b/MISC/TEST/fraction.cpp new file mode 100644 index 0000000..6c490d2 --- /dev/null +++ b/MISC/TEST/fraction.cpp @@ -0,0 +1,52 @@ +#include "../../base/base.h" +#include "fraction.h" + +bool bAutoReduce = true; +int nDefaultPrecision = DEFAULT_PRECISION; + +void Fraction::Reduce(void) { + int nTempNum, nTempDen; + if (nDen < 0) { + nDen = -nDen; + nNum = -nNum; + } + nTempNum = ABS(nNum); + nTempDen = nDen; + while (nTempNum != 0 && nTempDen != 0) { + if (nTempNum > nTempDen) { + nTempNum %= nTempDen; + } else { + nTempDen %= nTempNum; + } + } + if (nTempNum == 0) { + nTempNum = nTempDen; + } + nNum /= nTempNum; + nDen /= nTempNum; +} + +const int MAX_PRECISION = 32; + +Fraction::Fraction(double df, int nPrecision) { + int i, nTempNum, nTempDen; + int nSequence[MAX_PRECISION]; + for (i = 0; i <= nPrecision; i ++) { + nSequence[i] = (int) (df + (df < 0 ? -0.5 : 0.5)); + df -= nSequence[i]; + df = 1.0 / df; + } + nTempNum = nSequence[nPrecision]; + nTempDen = 1; + for (i = nPrecision - 1; i >= 0; i --) { + SWAP(nTempNum, nTempDen); + nTempNum += nTempDen * nSequence[i]; + } + if (nTempDen < 0) { + nNum = -nTempNum; + nDen = -nTempDen; + } else { + nNum = nTempNum; + nDen = nTempDen; + } +} diff --git a/MISC/TEST/fraction.h b/MISC/TEST/fraction.h new file mode 100644 index 0000000..b29078e --- /dev/null +++ b/MISC/TEST/fraction.h @@ -0,0 +1,203 @@ +#ifndef FRACTION_H +#define FRACTION_H + +const bool SKIP_REDUCE = true; +const int DEFAULT_PRECISION = 4; + +extern bool bAutoReduce; +extern int nDefaultPrecision; + +struct Fraction { + int nNum, nDen; + + void Reduce(void); + Fraction(void) { + // Do Nothing + } + Fraction(int n1, int n2 = 1, bool bSkipReduce = false) { + nNum = n1; + nDen = n2; + if (bAutoReduce && !bSkipReduce) { + Reduce(); + } + } + Fraction(double df, int nPrecision = nDefaultPrecision); + Fraction operator +(void) const { + return *this; + } + operator int(void) const { + return nNum / nDen; + } + operator double(void) const { + return (double) nNum / nDen; + } + Fraction operator -(void) const { + return Fraction(-nNum, nDen, SKIP_REDUCE); + } + int operator <(Fraction fract) const { + return nDen * fract.nDen < 0 ? nNum * fract.nDen > fract.nNum * nDen : nNum * fract.nDen < fract.nNum * nDen; + } + int operator <=(Fraction fract) const { + return nDen * fract.nDen < 0 ? nNum * fract.nDen >= fract.nNum * nDen : nNum * fract.nDen <= fract.nNum * nDen; + } + int operator >(Fraction fract) const { + return nDen * fract.nDen < 0 ? nNum * fract.nDen < fract.nNum * nDen : nNum * fract.nDen > fract.nNum * nDen; + } + int operator >=(Fraction fract) const { + return nDen * fract.nDen < 0 ? nNum * fract.nDen <= fract.nNum * nDen : nNum * fract.nDen >= fract.nNum * nDen; + } + int operator ==(Fraction fract) const { + return nNum * fract.nDen == fract.nNum * nDen; + } + int operator !=(Fraction fract) const { + return nNum * fract.nDen != fract.nNum * nDen; + } + int operator <(int n) const { + return nDen < 0 ? nNum > n * nDen : nNum < n * nDen; + } + int operator <=(int n) const { + return nDen < 0 ? nNum >= n * nDen : nNum <= n * nDen; + } + int operator >(int n) const { + return nDen < 0 ? nNum < n * nDen : nNum > n * nDen; + } + int operator >=(int n) const { + return nDen < 0 ? nNum <= n * nDen : nNum >= n * nDen; + } + int operator ==(int n) const { + return nNum == n * nDen; + } + int operator !=(int n) const { + return nNum != n * nDen; + } + Fraction operator +(Fraction fract) const { + return Fraction(nNum * fract.nDen + fract.nNum * nDen, nDen * fract.nDen); + } + Fraction operator -(Fraction fract) const { + return Fraction(nNum * fract.nDen - fract.nNum * nDen, nDen * fract.nDen); + } + Fraction operator *(Fraction fract) const { + return Fraction(nNum * fract.nNum, nDen * fract.nDen); + } + Fraction operator /(Fraction fract) const { + return Fraction(nNum * fract.nDen, fract.nNum * nDen); + } + Fraction operator +(int n) const { + return Fraction(nNum + n * nDen, nDen, SKIP_REDUCE); + } + Fraction operator -(int n) const { + return Fraction(nNum - n * nDen, nDen, SKIP_REDUCE); + } + Fraction operator *(int n) const { + return Fraction(nNum * n, nDen); + } + Fraction operator /(int n) const { + return Fraction(nNum, nDen * n); + } + Fraction &operator +=(Fraction fract) { + nNum *= fract.nDen; + nNum += fract.nNum * nDen; + nDen *= fract.nDen; + if (bAutoReduce) { + Reduce(); + } + return *this; + } + Fraction &operator -=(Fraction fract) { + nNum *= fract.nDen; + nNum -= fract.nNum * nDen; + nDen *= fract.nDen; + if (bAutoReduce) { + Reduce(); + } + return *this; + } + Fraction &operator *=(Fraction fract) { + nNum *= fract.nNum; + nDen *= fract.nDen; + if (bAutoReduce) { + Reduce(); + } + return *this; + } + Fraction &operator /=(Fraction fract) { + nNum *= fract.nDen; + nDen *= fract.nNum; + if (bAutoReduce) { + Reduce(); + } + return *this; + } + Fraction &operator +=(int n) { + nNum += n * nDen; + return *this; + } + Fraction &operator -=(int n) { + nNum -= n * nDen; + return *this; + } + Fraction &operator *=(int n) { + nNum *= n; + if (bAutoReduce) { + Reduce(); + } + return *this; + } + Fraction &operator /=(int n) { + nDen *= n; + if (bAutoReduce) { + Reduce(); + } + return *this; + } + Fraction operator ++(void) { + nNum += nDen; + return *this; + } + Fraction operator --(void) { + nNum -= nDen; + return *this; + } +}; // fract + +inline int operator <(int n, Fraction fract) { + return fract.nDen < 0 ? n * fract.nDen > fract.nNum : n * fract.nDen < fract.nNum; +} + +inline int operator <=(int n, Fraction fract) { + return fract.nDen < 0 ? n * fract.nDen >= fract.nNum : n * fract.nDen <= fract.nNum; +} + +inline int operator >(int n, Fraction fract) { + return fract.nDen < 0 ? n * fract.nDen < fract.nNum : n * fract.nDen > fract.nNum; +} + +inline int operator >=(int n, Fraction fract) { + return fract.nDen < 0 ? n * fract.nDen <= fract.nNum : n * fract.nDen >= fract.nNum; +} + +inline int operator ==(int n, Fraction fract) { + return n * fract.nDen == fract.nNum; +} + +inline int operator !=(int n, Fraction fract) { + return n * fract.nDen != fract.nNum; +} + +inline Fraction operator +(int n, Fraction fract) { + return Fraction(n * fract.nDen + fract.nNum, fract.nDen, SKIP_REDUCE); +} + +inline Fraction operator -(int n, Fraction fract) { + return Fraction(n * fract.nDen - fract.nNum, fract.nDen, SKIP_REDUCE); +} + +inline Fraction operator *(int n, Fraction fract) { + return Fraction(n * fract.nNum, fract.nDen); +} + +inline Fraction operator /(int n, Fraction fract) { + return Fraction(n * fract.nDen, fract.nNum); +} + +#endif diff --git a/MISC/TEST/genprime.cpp b/MISC/TEST/genprime.cpp new file mode 100644 index 0000000..f066815 --- /dev/null +++ b/MISC/TEST/genprime.cpp @@ -0,0 +1,38 @@ +#include +#include +#include +#include "../../base/base.h" + +const uint32_t MAX_DIVISOR = 65536; +const uint32_t TABLE_LEN = MAX_DIVISOR / 16 * MAX_DIVISOR; + +int main(void) { + uint32_t i, j, k; + uint8_t *ucPrimeTable; + int64_t llTime; + + ucPrimeTable = new uint8_t[TABLE_LEN]; + llTime = GetTime(); + printf("Generating prime numbers below %d^2...\n", (int) MAX_DIVISOR); + memset(ucPrimeTable, 0xff, TABLE_LEN); + k = 4; + for (i = 3; i < MAX_DIVISOR; i += 2) { + if (i > k) { + printf("Numbers below %d are sifted, using %dms.\n", (int) k, (int) (GetTime() - llTime)); + k <<= 1; + } + if (ucPrimeTable[i / 16] & (1 << ((i / 2) % 8))) { + for (j = i * 3 / 2; j < TABLE_LEN * 8; j += i) { + ucPrimeTable[j / 8] &= ~(1 << (j % 8)); + } + } + } + j = 0; + for (i = 0; i < TABLE_LEN / 4; i ++) { + j += PopCnt32(((uint32_t *) ucPrimeTable)[i]); + } + printf("Numbers below %d are sifted, using %dms.\n", (int) MAX_DIVISOR, (int) (GetTime() - llTime)); + printf("There are %d prime numbers below %d^2.\n", (int) j, (int) MAX_DIVISOR); + delete[] ucPrimeTable; + return 0; +} diff --git a/MISC/TEST/makefile.sh b/MISC/TEST/makefile.sh new file mode 100644 index 0000000..2f90f42 --- /dev/null +++ b/MISC/TEST/makefile.sh @@ -0,0 +1,3 @@ +g++ -O4 -Wall -oGENPRIME.EXE genprime.cpp +g++ -O4 -Wall -oNETWORK.EXE matrix.cpp network.cpp +g++ -O4 -Wall -oNETWORK2.EXE matrix.cpp network2.cpp \ No newline at end of file diff --git a/MISC/TEST/matrix.cpp b/MISC/TEST/matrix.cpp new file mode 100644 index 0000000..719d8cf --- /dev/null +++ b/MISC/TEST/matrix.cpp @@ -0,0 +1,296 @@ +#include "../../base/base.h" +#include "matrix.h" + +Matrix Matrix::DelRowCol(int nRowStart, int nRowLen, int nColStart, int nColLen) const { + Matrix matrRet(nRow - nRowLen, nCol - nColLen); + matrRet.Load(0, 0, *this, 0, 0, nRowStart, nColStart); + matrRet.Load(nRowStart, 0, *this, nRowStart + nRowLen, 0, nRow - nRowStart - nRowLen, nColStart); + matrRet.Load(0, nColStart, *this, 0, nColStart + nColLen, nRowStart, nCol - nColStart - nColLen); + matrRet.Load(nRowStart, nColStart, *this, nRowStart + nRowLen, nColStart + nColLen, + nRow - nRowStart - nRowLen, nCol - nColStart - nColLen); + return matrRet; +} + +Matrix Matrix::DelRow(int nStart, int nLen) const { + Matrix matrRet(nRow - nLen, nCol); + matrRet.Load(0, 0, *this, 0, 0, nStart, nCol); + matrRet.Load(nStart, 0, *this, nStart + nLen, 0, nRow - nStart - nLen, nCol); + return matrRet; +} + +Matrix Matrix::DelCol(int nStart, int nLen) const { + Matrix matrRet(nRow, nCol - nLen); + matrRet.Load(0, 0, *this, 0, 0, nRow, nStart); + matrRet.Load(0, nStart, *this, 0, nStart + nLen, nRow, nCol - nStart - nLen); + return matrRet; +} + +Matrix Matrix::operator -(void) const { + int i, j; + Matrix matrRet(nRow, nCol); + for (i = 0; i < nRow; i ++) { + for (j = 0; j < nCol; j ++) { + matrRet[i][j] = -(*this)[i][j]; + } + } + return matrRet; +} + +Matrix Matrix::operator +(const Matrix &matr) const { + int i, j; + Matrix matrRet(nRow, nCol); + for (i = 0; i < nRow; i ++) { + for (j = 0; j < nCol; j ++) { + matrRet[i][j] = (*this)[i][j] + matr[i][j]; + } + } + return matrRet; +} + +Matrix Matrix::operator -(const Matrix &matr) const { + int i, j; + Matrix matrRet(nRow, nCol); + for (i = 0; i < nRow; i ++) { + for (j = 0; j < nCol; j ++) { + matrRet[i][j] = (*this)[i][j] - matr[i][j]; + } + } + return matrRet; +} + +Matrix Matrix::operator *(double dfReal) const { + int i, j; + Matrix matrRet(nRow, nCol); + for (i = 0; i < nRow; i ++) { + for (j = 0; j < nCol; j ++) { + matrRet[i][j] = (*this)[i][j] * dfReal; + } + } + return matrRet; +} + +Matrix Matrix::operator /(double dfReal) const { + int i, j; + Matrix matrRet(nRow, nCol); + for (i = 0; i < nRow; i ++) { + for (j = 0; j < nCol; j ++) { + matrRet[i][j] = (*this)[i][j] / dfReal; + } + } + return matrRet; +} + +Matrix Matrix::operator *(const Matrix &matr) const { + int i, j, k; + Matrix matrRet(nRow, matr.nCol); + for (i = 0; i < nRow; i ++) { + for (j = 0; j < matr.nCol; j ++) { + for (k = 0; k < nCol; k ++) { + matrRet[i][j] += (*this)[i][k] * matr[k][j]; + } + } + } + return matrRet; +} + +Matrix Matrix::Trans(void) const { + int i, j; + Matrix matrRet(nCol, nRow); + for (i = 0; i < nCol; i ++) { + for (j = 0; j < nRow; j ++) { + matrRet[i][j] = (*this)[j][i]; + } + } + return matrRet; +} + +double Matrix::Det(void) const { + int i, j, jMax; + double dfRet, dfThis, dfMax; + Matrix matrTmp(*this); + dfRet = 1.0; + for (i = 0; i < nRow; i ++) { + // Step 1: + dfMax = 0.0; + jMax = i; + for (j = i; j < nRow; j ++) { + dfThis = ABS(matrTmp[i][j]); + if (dfThis > dfMax) { + jMax = j; + dfMax = dfThis; + } + } + if (dfMax == 0.0) { + return 0.0; + } + if (jMax != i) { + dfRet = -dfRet; + matrTmp.ColSwap(i, jMax); + } + // Step 2: + dfRet *= matrTmp[i][i]; + matrTmp.RowMul(i, 1.0 / matrTmp[i][i], i); + for (j = i + 1; j < nRow; j ++) { + if (matrTmp[j][i] != 0.0) { + dfRet *= matrTmp[j][i]; + matrTmp.RowMul(j, 1.0 / matrTmp[j][i], i); + matrTmp.RowSub(j, i, i); + } + } + } + return dfRet; +} + +Matrix Matrix::LeftDiv(const Matrix &matr) const { + int i, j, jMax; + double dfThis, dfMax; + int *nSwapList; + Matrix matrRet(*this), matrTmp(matr); + nSwapList = new int[nRow]; + for (i = 0; i < nRow; i ++) { + // Step 1: + dfMax = 0.0; + jMax = i; + for (j = i; j < nRow; j ++) { + dfThis = ABS(matrTmp[i][j]); + if (dfThis > dfMax) { + jMax = j; + dfMax = dfThis; + } + } + if (dfMax == 0.0) { + delete[] nSwapList; + return Matrix(nRow, nRow); + } + nSwapList[i] = jMax; + if (jMax != i) { + matrTmp.ColSwap(i, jMax); + } + // Step 2: + matrRet.RowMul(i, 1.0 / matrTmp[i][i]); + matrTmp.RowMul(i, 1.0 / matrTmp[i][i], i); + for (j = i + 1; j < nRow; j ++) { + if (matrTmp[j][i] != 0.0) { + matrRet.RowMul(j, 1.0 / matrTmp[j][i]); + matrRet.RowSub(j, i); + matrTmp.RowMul(j, 1.0 / matrTmp[j][i], i); + matrTmp.RowSub(j, i, i); + } + } + } + // Step 3: + for (i = nRow - 1; i >= 0; i --) { + for (j = i - 1; j >= 0; j --) { + if (matrTmp[j][i] != 0.0) { + matrRet.RowSubMul(j, i, matrTmp[j][i]); + } + } + } + // Step 4: + for (i = nRow - 1; i >= 0; i --) { + if (nSwapList[i] != i) { + matrRet.RowSwap(i, nSwapList[i]); + } + } + delete[] nSwapList; + return matrRet; +} + +Matrix Matrix::RightDiv(const Matrix &matr) const { + int i, j, jMax; + double dfThis, dfMax; + int *nSwapList; + Matrix matrRet(*this), matrTmp(matr); + nSwapList = new int[nCol]; + for (i = 0; i < nCol; i ++) { + // Step 1: + dfMax = 0.0; + jMax = i; + for (j = i; j < nCol; j ++) { + dfThis = ABS(matrTmp[j][i]); + if (dfThis > dfMax) { + jMax = j; + dfMax = dfThis; + } + } + if (dfMax == 0.0) { + delete[] nSwapList; + return Matrix(nCol, nCol); + } + nSwapList[i] = jMax; + if (jMax != i) { + matrTmp.RowSwap(i, jMax); + } + // Step 2: + matrRet.ColMul(i, 1.0 / matrTmp[i][i]); + matrTmp.ColMul(i, 1.0 / matrTmp[i][i], i); + for (j = i + 1; j < nCol; j ++) { + if (matrTmp[i][j] != 0.0) { + matrRet.ColMul(j, 1.0 / matrTmp[i][j]); + matrRet.ColSub(j, i); + matrTmp.ColMul(j, 1.0 / matrTmp[i][j], i); + matrTmp.ColSub(j, i, i); + } + } + } + // Step 3: + for (i = nCol - 1; i >= 0; i --) { + for (j = i - 1; j >= 0; j --) { + if (matrTmp[i][j] != 0.0) { + matrRet.ColSubMul(j, i, matrTmp[i][j]); + } + } + } + // Step 4: + for (i = nCol - 1; i >= 0; i --) { + if (nSwapList[i] != i) { + matrRet.ColSwap(i, nSwapList[i]); + } + } + delete[] nSwapList; + return matrRet; +} + +Matrix Matrix::Inv(void) const { + int i; + Matrix matrRet(nRow, nRow); + for (i = 0; i < nRow; i ++) { + matrRet[i][i] = 1.0; + } + return matrRet.LeftDiv(*this); +} + +Matrix operator *(double dfReal, const Matrix &matr) { + int i, j; + Matrix matrRet(matr.nRow, matr.nCol); + for (i = 0; i < matr.nRow; i ++) { + for (j = 0; j < matr.nCol; j ++) { + matrRet[i][j] = matr[i][j] * dfReal; + } + } + return matrRet; +} + +Matrix Merge(const Matrix &matrTopLeft, const Matrix &matrBottomLeft, + const Matrix &matrTopRight, const Matrix &matrBottomRight) { + Matrix matrRet(matrTopLeft.nRow + matrBottomLeft.nRow, matrTopLeft.nCol + matrTopRight.nCol); + matrRet.Load(0, 0, matrTopLeft, 0, 0, matrTopLeft.nRow, matrTopLeft.nCol); + matrRet.Load(matrTopLeft.nRow, 0, matrBottomLeft, 0, 0, matrBottomLeft.nRow, matrBottomLeft.nCol); + matrRet.Load(0, matrTopLeft.nCol, matrTopRight, 0, 0, matrTopRight.nRow, matrTopRight.nCol); + matrRet.Load(matrTopLeft.nRow, matrTopLeft.nCol, matrBottomRight, 0, 0, matrBottomRight.nRow, matrBottomRight.nCol); + return matrRet; +} + +Matrix MergeRow(const Matrix &matrTop, const Matrix &matrBottom) { + Matrix matrRet(matrTop.nRow + matrBottom.nRow, matrTop.nCol); + matrRet.Load(0, 0, matrTop, 0, 0, matrTop.nRow, matrTop.nCol); + matrRet.Load(matrTop.nRow, 0, matrBottom, 0, 0, matrBottom.nRow, matrBottom.nCol); + return matrRet; +} + +Matrix MergeCol(const Matrix &matrLeft, const Matrix &matrRight) { + Matrix matrRet(matrLeft.nRow, matrLeft.nCol + matrRight.nCol); + matrRet.Load(0, 0, matrLeft, 0, 0, matrLeft.nRow, matrLeft.nCol); + matrRet.Load(0, matrLeft.nCol, matrRight, 0, 0, matrRight.nRow, matrRight.nCol); + return matrRet; +} diff --git a/MISC/TEST/matrix.h b/MISC/TEST/matrix.h new file mode 100644 index 0000000..f41d9ef --- /dev/null +++ b/MISC/TEST/matrix.h @@ -0,0 +1,239 @@ +#include +#include "../../base/base.h" + +#ifndef MATRIX_H +#define MATRIX_H + +struct Matrix { + int nRow, nCol; + double *lpdf; // Don't Use It! + + double *operator [](int nIndex) { + return lpdf + (nIndex * nCol); + } + + const double *operator [](int nIndex) const { + return lpdf + (nIndex * nCol); + } + + Matrix(void) { + lpdf = NULL; + } + + Matrix(int nRowLen, int nColLen) { + nRow = nRowLen; + nCol = nColLen; + lpdf = new double[nRow * nCol]; + memset(lpdf, 0, nRow * nCol * sizeof(double)); + } + + Matrix(const Matrix &matr) { + nRow = matr.nRow; + nCol = matr.nCol; + lpdf = new double[nRow * nCol]; + memcpy(lpdf, matr.lpdf, nRow * nCol * sizeof(double)); + } + + ~Matrix(void) { + if (lpdf != NULL) { + delete[] lpdf; + lpdf = NULL; + } + } + + Matrix &operator =(const Matrix &matr) { + if (lpdf != matr.lpdf) { + if (lpdf != NULL) { + delete[] lpdf; + lpdf = NULL; + } + nRow = matr.nRow; + nCol = matr.nCol; + lpdf = new double[nRow * nCol]; + memcpy(lpdf, matr.lpdf, nRow * nCol * sizeof(double)); + } + return *this; + } + + void Load(int nDstRow, int nDstCol, const Matrix &matrSrc, int nSrcRow, int nSrcCol, int nRowLen, int nColLen) { + int i, j; + for (i = 0; i < nRowLen; i ++) { + for (j = 0; j < nColLen; j ++) { + (*this)[nDstRow + i][nDstCol + j] = matrSrc[nSrcRow + i][nSrcCol + j]; + } + } + } + + Matrix Merge(const Matrix &matrBottomLeft, const Matrix &matrTopRight, const Matrix &matrBottomRight) const; + Matrix MergeRow(const Matrix &matrBottom) const; + Matrix MergeCol(const Matrix &matrRight) const; + Matrix DelRowCol(int nRowStart, int nRowLen, int nColStart, int nColLen) const; + Matrix DelRow(int nStart, int nLen) const; + Matrix DelCol(int nStart, int nLen) const; + + const Matrix &operator +(void) const { + return *this; + } + + Matrix operator -(void) const; + Matrix operator +(const Matrix &matr) const; + Matrix operator -(const Matrix &matr) const; + Matrix operator *(double dfReal) const; + Matrix operator /(double dfReal) const; + + Matrix &operator +=(const Matrix &matr) { + int i, j; + for (i = 0; i < nRow; i ++) { + for (j = 0; j < nCol; j ++) { + (*this)[i][j] += matr[i][j]; + } + } + return *this; + } + + Matrix &operator -=(const Matrix &matr) { + int i, j; + for (i = 0; i < nRow; i ++) { + for (j = 0; j < nCol; j ++) { + (*this)[i][j] -= matr[i][j]; + } + } + return *this; + } + + Matrix &operator *=(double dfReal) { + int i, j; + for (i = 0; i < nRow; i ++) { + for (j = 0; j < nCol; j ++) { + (*this)[i][j] *= dfReal; + } + } + return *this; + } + + Matrix &operator /=(double dfReal) { + int i, j; + for (i = 0; i < nRow; i ++) { + for (j = 0; j < nCol; j ++) { + (*this)[i][j] /= dfReal; + } + } + return *this; + } + + Matrix operator *(const Matrix &matr) const; + + void RowSwap(int nDstRow, int nSrcRow, int nStart = 0) { + int i; + if (nDstRow != nSrcRow) { + for (i = nStart; i < nCol; i ++) { + SWAP((*this)[nDstRow][i], (*this)[nSrcRow][i]); + } + } + } + + void RowMul(int nDstRow, double dfReal, int nStart = 0) { + int i; + for (i = nStart; i < nCol; i ++) { + (*this)[nDstRow][i] *= dfReal; + } + } + + void RowAdd(int nDstRow, int nSrcRow, int nStart = 0) { + int i; + for (i = nStart; i < nCol; i ++) { + (*this)[nDstRow][i] += (*this)[nSrcRow][i]; + } + } + + void RowAddMul(int nDstRow, int nSrcRow, double dfReal, int nStart = 0) { + int i; + for (i = nStart; i < nCol; i ++) { + (*this)[nDstRow][i] += (*this)[nSrcRow][i] * dfReal; + } + } + + void RowSub(int nDstRow, int nSrcRow, int nStart = 0) { + int i; + for (i = nStart; i < nCol; i ++) { + (*this)[nDstRow][i] -= (*this)[nSrcRow][i]; + } + } + + void RowSubMul(int nDstRow, int nSrcRow, double dfReal, int nStart = 0) { + int i; + for (i = nStart; i < nCol; i ++) { + (*this)[nDstRow][i] -= (*this)[nSrcRow][i] * dfReal; + } + } + + void ColSwap(int nDstCol, int nSrcCol, int nStart = 0) { + int i; + if (nDstCol != nSrcCol) { + for (i = nStart; i < nRow; i ++) { + SWAP((*this)[i][nDstCol], (*this)[i][nSrcCol]); + } + } + } + + void ColMul(int nDstCol, double dfReal, int nStart = 0) { + int i; + for (i = nStart; i < nRow; i ++) { + (*this)[i][nDstCol] *= dfReal; + } + } + + void ColAdd(int nDstCol, int nSrcCol, int nStart = 0) { + int i; + for (i = nStart; i < nRow; i ++) { + (*this)[i][nDstCol] += (*this)[i][nSrcCol]; + } + } + + void ColAddMul(int nDstCol, int nSrcCol, double dfReal, int nStart = 0) { + int i; + for (i = nStart; i < nRow; i ++) { + (*this)[i][nDstCol] += (*this)[i][nSrcCol] * dfReal; + } + } + + void ColSub(int nDstCol, int nSrcCol, int nStart = 0) { + int i; + for (i = nStart; i < nRow; i ++) { + (*this)[i][nDstCol] -= (*this)[i][nSrcCol]; + } + } + + void ColSubMul(int nDstCol, int nSrcCol, double dfReal, int nStart = 0) { + int i; + for (i = nStart; i < nRow; i ++) { + (*this)[i][nDstCol] -= (*this)[i][nSrcCol] * dfReal; + } + } + + Matrix Trans(void) const; + double Det(void) const; + Matrix LeftDiv(const Matrix &matr) const; + Matrix RightDiv(const Matrix &matr) const; + Matrix Inv(void) const; +}; // matr + +inline Matrix Trans(const Matrix &matr) { + return matr.Trans(); +} + +inline double Det(const Matrix &matr) { + return matr.Det(); +} + +inline Matrix Inv(const Matrix &matr) { + return matr.Inv(); +} + +Matrix operator *(double dfReal, const Matrix &matr); +Matrix Merge(const Matrix &matrTopLeft, const Matrix &matrBottomLeft, + const Matrix &matrTopRight, const Matrix &matrBottomRight); +Matrix MergeRow(const Matrix &matrTop, const Matrix &matrBottom); +Matrix MergeCol(const Matrix &matrLeft, const Matrix &matrRight); + +#endif diff --git a/MISC/TEST/network.cpp b/MISC/TEST/network.cpp new file mode 100644 index 0000000..ed2bf54 --- /dev/null +++ b/MISC/TEST/network.cpp @@ -0,0 +1,50 @@ +#include +#include "../../base/base.h" +#include "matrix.h" + +double Network(int nLength) { + int nMatLen = nLength * nLength; + int nInDot, nOutDot, i, j; + double dfCondSum; + Matrix mtCurr(nMatLen, 1); + Matrix mtCond(nMatLen, nMatLen); + nOutDot = nLength * (nLength + 1) / 2; + nInDot = nOutDot - nLength - 1; + mtCurr[nInDot][0] = 1.0; + mtCurr[nOutDot][0] = -1.0; + for (i = 0; i < nLength - 1; i ++) { + for (j = 0; j < nLength - 1; j ++) { + mtCond[i * nLength + j][(i + 1) * nLength + j] = 1.0; + mtCond[(i + 1) * nLength + j][i * nLength + j] = 1.0; + mtCond[i * nLength + j][i * nLength + j + 1] = 1.0; + mtCond[i * nLength + j + 1][i * nLength + j] = 1.0; + } + mtCond[(nLength - 1) * nLength + i][(nLength - 1) * nLength + i + 1] = 1.0; + mtCond[(nLength - 1) * nLength + i + 1][(nLength - 1) * nLength + i] = 1.0; + mtCond[i * nLength + nLength - 1][(i + 1) * nLength + nLength - 1] = 1.0; + mtCond[(i + 1) * nLength + nLength - 1][i * nLength + nLength - 1] = 1.0; + } + for (i = 0; i < nMatLen; i ++) { + dfCondSum = 0.0; + for (j = 0; j < nMatLen; j ++) { + dfCondSum -= mtCond[i][j]; + } + mtCond[i][i] = dfCondSum; + } + for (i = 0; i < nMatLen; i ++) { + mtCond[nInDot][i] = 0.0; + mtCond[i][nInDot] = 0.0; + } + mtCond[nInDot][nInDot] = 1.0; + return mtCurr.LeftDiv(mtCond)[nOutDot][0]; +} + +int main(void) { + int i; + int64_t llTime; + llTime = GetTime(); + for (i = 2; i <= 64; i += 2) { + printf("R(%d)=%.15f(%dms)\n", i, Network(i), (int) (GetTime() - llTime)); + } + return 0; +} diff --git a/MISC/TEST/network2.cpp b/MISC/TEST/network2.cpp new file mode 100644 index 0000000..f437c88 --- /dev/null +++ b/MISC/TEST/network2.cpp @@ -0,0 +1,117 @@ +#include +#include "matrix.h" + +double Network1(int nHeight, int nWidth) { + int nMatLen = nWidth * nHeight + 1; + int nInDot, nOutDot, i, j; + double dfCondSum; + Matrix mtCurr(nMatLen, 1); + Matrix mtCond(nMatLen, nMatLen); + nOutDot = nWidth * nHeight; + nInDot = nOutDot - 1; + mtCurr[nInDot][0] = 1.0; + mtCurr[nOutDot][0] = -1.0; + for (i = 0; i < nHeight - 1; i ++) { + for (j = 0; j < nWidth - 1; j ++) { + mtCond[i * nWidth + j][(i + 1) * nWidth + j] = 2.0; + mtCond[(i + 1) * nWidth + j][i * nWidth + j] = 2.0; + mtCond[i * nWidth + j][i * nWidth + j + 1] = 2.0; + mtCond[i * nWidth + j + 1][i * nWidth + j] = 2.0; + } + mtCond[i * nWidth + nWidth - 1][(i + 1) * nWidth + nWidth - 1] = 2.0; + mtCond[(i + 1) * nWidth + nWidth - 1][i * nWidth + nWidth - 1] = 2.0; + mtCond[i * nWidth + nWidth - 1][nOutDot] = 4.0; + mtCond[nOutDot][i * nWidth + nWidth - 1] = 4.0; + } + for (j = 0; j < nWidth - 1; j ++) { + mtCond[(nHeight - 1) * nWidth + j][(nHeight - 1) * nWidth + j + 1] = 1.0; + mtCond[(nHeight - 1) * nWidth + j + 1][(nHeight - 1) * nWidth + j] = 1.0; + } + mtCond[nInDot][nOutDot] = 2.0; + mtCond[nOutDot][nInDot] = 2.0; + for (i = 0; i < nMatLen; i ++) { + dfCondSum = 0.0; + for (j = 0; j < nMatLen; j ++) { + dfCondSum -= mtCond[i][j]; + } + mtCond[i][i] = dfCondSum; + } + for (i = 0; i < nMatLen; i ++) { + mtCond[nInDot][i] = 0.0; + mtCond[i][nInDot] = 0.0; + } + mtCond[nInDot][nInDot] = 1.0; + return mtCurr.LeftDiv(mtCond)[nOutDot][0] * 2; +} + +double Network2(int nHeight, int nWidth) { + int nMatLen = nWidth * nHeight + 1; + int nInDot, nOutDot, i, j; + double dfCondSum; + Matrix mtCurr(nMatLen, 1); + Matrix mtCond(nMatLen, nMatLen); + nOutDot = nWidth * nHeight; + nInDot = nOutDot - 1; + mtCurr[nInDot][0] = 1.0; + mtCurr[nOutDot][0] = -1.0; + for (i = 0; i < nHeight - 1; i ++) { + for (j = 0; j < nWidth - 1; j ++) { + mtCond[i * nWidth + j][(i + 1) * nWidth + j] = 1.0; + mtCond[(i + 1) * nWidth + j][i * nWidth + j] = 1.0; + mtCond[i * nWidth + j][i * nWidth + j + 1] = 1.0; + mtCond[i * nWidth + j + 1][i * nWidth + j] = 1.0; + mtCond[i * nWidth + j][(i + 1) * nWidth + j + 1] = 0.5; + mtCond[(i + 1) * nWidth + j + 1][i * nWidth + j] = 0.5; + mtCond[i * nWidth + j + 1][(i + 1) * nWidth + j] = 0.5; + mtCond[(i + 1) * nWidth + j][i * nWidth + j + 1] = 0.5; + } + mtCond[i * nWidth + nWidth - 1][(i + 1) * nWidth + nWidth - 1] = 0.5; + mtCond[(i + 1) * nWidth + nWidth - 1][i * nWidth + nWidth - 1] = 0.5; + mtCond[i * nWidth + nWidth - 1][nOutDot] = 4.0; + mtCond[nOutDot][i * nWidth + nWidth - 1] = 4.0; + } + for (j = 0; j < nWidth - 1; j ++) { + mtCond[(nHeight - 1) * nWidth + j][(nHeight - 1) * nWidth + j + 1] = 0.5; + mtCond[(nHeight - 1) * nWidth + j + 1][(nHeight - 1) * nWidth + j] = 0.5; + } + mtCond[nInDot][nOutDot] = 2.0; + mtCond[nOutDot][nInDot] = 2.0; + for (i = 0; i < nMatLen; i ++) { + dfCondSum = 0.0; + for (j = 0; j < nMatLen; j ++) { + dfCondSum -= mtCond[i][j]; + } + mtCond[i][i] = dfCondSum; + } + for (i = 0; i < nMatLen; i ++) { + mtCond[nInDot][i] = 0.0; + mtCond[i][nInDot] = 0.0; + } + mtCond[nInDot][nInDot] = 1.0; + return mtCurr.LeftDiv(mtCond)[nOutDot][0] * 2; +} + +const int ADDIT_HEIGHT = 10; + +int main(void) { + int nHeight, nWidth; + double dfR1, dfR2, dfR3, dfLastR2, dfLastR3; + int64_t llTime; + nWidth = 1; + nHeight = nWidth + ADDIT_HEIGHT; + dfLastR2 = Network2(nHeight, nWidth); + dfLastR3 = dfLastR2 * 0.5 / Network1(nHeight, nWidth); + for (nWidth ++; nWidth < 70; nWidth ++) { + nHeight = nWidth + ADDIT_HEIGHT; + llTime = GetTime(); + dfR1 = Network1(nHeight, nWidth); + dfR2 = Network2(nHeight, nWidth); + dfR3 = dfR2 * 0.5 / dfR1; + dfLastR2 -= dfR2; + dfLastR3 -= dfR3; + printf("R=%.15f(%dx%d,%dms)\n", (dfR3 * dfLastR2 - dfR2 * dfLastR3) / (dfLastR2 - dfLastR3), nHeight * 2 + 1, nWidth * 2, (int) (GetTime() - llTime)); + dfLastR2 = dfR2; + dfLastR3 = dfR3; + } + return 0; +} diff --git "a/MISC/TEST/\347\224\265\351\230\273\347\275\221\347\273\234.DOC" "b/MISC/TEST/\347\224\265\351\230\273\347\275\221\347\273\234.DOC" new file mode 100644 index 0000000..4953473 Binary files /dev/null and "b/MISC/TEST/\347\224\265\351\230\273\347\275\221\347\273\234.DOC" differ diff --git a/MISC/TESTPIPE/TESTPIPE.BAS b/MISC/TESTPIPE/TESTPIPE.BAS new file mode 100644 index 0000000..192dec9 --- /dev/null +++ b/MISC/TESTPIPE/TESTPIPE.BAS @@ -0,0 +1,26 @@ +Attribute VB_Name = "mdlTestPipe" +Option Explicit + +Public pipe As PipeStruct, bRunning As Boolean + +Public Sub Main() + +Dim lpStr As Long +Dim szFileName As String +szFileName = OpenFileDialog("Load Executive File", "Executive Files (*.EXE)|*.EXE|All Files (*.*)|*.*") +If szFileName <> "" Then + PipeOpen pipe, szFileName + frmTestPipe.Show + bRunning = True + Do While bRunning + Sleep 1 + DoEvents + lpStr = PipeLineInput(pipe) + If lpStr <> 0 Then + frmTestPipe.lstOutput.AddItem MkBStr(lpStr) + frmTestPipe.lstOutput.ListIndex = frmTestPipe.lstOutput.ListCount - 1 + End If + Loop +End If + +End Sub diff --git a/MISC/TESTPIPE/TESTPIPE.FRM b/MISC/TESTPIPE/TESTPIPE.FRM new file mode 100644 index 0000000..b5df36c --- /dev/null +++ b/MISC/TESTPIPE/TESTPIPE.FRM @@ -0,0 +1,60 @@ +VERSION 5.00 +Begin VB.Form frmTestPipe + BorderStyle = 1 'Fixed Single + Caption = "Pipe Test" + ClientHeight = 3195 + ClientLeft = 45 + ClientTop = 330 + ClientWidth = 4680 + MaxButton = 0 'False + ScaleHeight = 3195 + ScaleWidth = 4680 + StartUpPosition = 3 'Windows Default + Begin VB.ListBox lstOutput + Height = 2595 + ItemData = "TESTPIPE.frx":0000 + Left = 120 + List = "TESTPIPE.frx":0002 + TabIndex = 2 + Top = 480 + Width = 4455 + End + Begin VB.CommandButton btnSend + Caption = "Send" + Height = 375 + Left = 3720 + TabIndex = 1 + Top = 0 + Width = 855 + End + Begin VB.TextBox txtSend + Height = 285 + Left = 120 + TabIndex = 0 + Top = 120 + Width = 3495 + End +End +Attribute VB_Name = "frmTestPipe" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False +Option Explicit + +Private Sub btnSend_Click() + +If txtSend.Text = vbNullString Then + PipeLineOutput pipe, "" +Else + PipeLineOutput pipe, txtSend.Text +End If +txtSend.Text = "" + +End Sub + +Private Sub Form_UnLoad(nCancel As Integer) + +bRunning = False + +End Sub diff --git a/MISC/TESTPIPE/TESTPIPE.FRX b/MISC/TESTPIPE/TESTPIPE.FRX new file mode 100644 index 0000000..593f470 Binary files /dev/null and b/MISC/TESTPIPE/TESTPIPE.FRX differ diff --git a/MISC/TESTPIPE/TESTPIPE.VBP b/MISC/TESTPIPE/TESTPIPE.VBP new file mode 100644 index 0000000..3ace7b6 --- /dev/null +++ b/MISC/TESTPIPE/TESTPIPE.VBP @@ -0,0 +1,40 @@ +Type=Exe +Form=TESTPIPE.FRM +Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\WINDOWS\system32\stdole2.tlb#OLE Automation +Module=mdlPipe; ..\..\UTILITY\PIPE.BAS +Module=mdlTestPipe; TESTPIPE.BAS +Module=mdlBase; ..\..\utility\BASE.BAS +IconForm="frmTestPipe" +Startup="Sub Main" +HelpFile="" +Title="Pipe Test" +ExeName32="TESTPIPE.EXE" +Path32="..\..\BIN" +Command32="" +Name="prjTestPipe" +HelpContextID="0" +CompatibleMode="0" +MajorVer=1 +MinorVer=0 +RevisionVer=0 +AutoIncrementVer=0 +ServerSupportFiles=0 +VersionCompanyName="Unknown Organization" +CompilationType=0 +OptimizationType=0 +FavorPentiumPro(tm)=0 +CodeViewDebugInfo=0 +NoAliasing=0 +BoundsCheck=0 +OverflowCheck=0 +FlPointCheck=0 +FDIVCheck=0 +UnroundedFP=0 +StartMode=0 +Unattended=0 +Retained=0 +ThreadPerObject=0 +MaxNumberOfThreads=1 + +[MS Transaction Server] +AutoRefresh=1 diff --git a/MISC/VBCHAT/VBCHATC.BAS b/MISC/VBCHAT/VBCHATC.BAS new file mode 100644 index 0000000..1afeb6e --- /dev/null +++ b/MISC/VBCHAT/VBCHATC.BAS @@ -0,0 +1,45 @@ +Attribute VB_Name = "mdlVBChat" +Option Explicit + +Public Const BUFFER_LEN As Long = 65536 + +Public bRunning As Boolean, bCtrlEnter As Boolean +Public nSocket As Long + +Sub Main() + +Dim nLen As Long +Dim ucBuffer(0 To BUFFER_LEN - 1) As Byte + +WSBStartup +nSocket = INVALID_SOCKET +bRunning = True +bCtrlEnter = False +frmVBChat.Show +Do While bRunning + If nSocket = INVALID_SOCKET Then + Sleep 1 + Else + nLen = WSBRecv(nSocket, VarPtr(ucBuffer(0)), BUFFER_LEN) + If nLen < 0 Then + WSBDisconnect nSocket + nSocket = INVALID_SOCKET + MsgBox "Server Disconnected", vbInformation + frmVBChat.btnConnect.Caption = "Connect" + frmVBChat.btnSend.Enabled = False + ElseIf nLen = 0 Then + Sleep 1 + Else + frmVBChat.txtRecv.Text = frmVBChat.txtRecv.Text + SysAllocStringByteLen(VarPtr(ucBuffer(0)), nLen) + frmVBChat.txtRecv.SelStart = Len(frmVBChat.txtRecv.Text) + If frmVBChat.chkQuiet.Value = 0 Then + MessageBeep MB_ICONINFORMATION + frmVBChat.SetFocus + End If + End If + End If + DoEvents +Loop +WSBCleanup + +End Sub diff --git a/MISC/VBCHAT/VBCHATC.FRM b/MISC/VBCHAT/VBCHATC.FRM new file mode 100644 index 0000000..e03a4b7 --- /dev/null +++ b/MISC/VBCHAT/VBCHATC.FRM @@ -0,0 +1,255 @@ +VERSION 5.00 +Begin VB.Form frmVBChat + BorderStyle = 1 'Fixed Single + Caption = "VBChat Client" + ClientHeight = 3195 + ClientLeft = 45 + ClientTop = 330 + ClientWidth = 4680 + BeginProperty Font + Name = "宋体" + Size = 9 + Charset = 134 + Weight = 400 + Underline = 0 'False + Italic = 0 'False + Strikethrough = 0 'False + EndProperty + MaxButton = 0 'False + ScaleHeight = 3195 + ScaleWidth = 4680 + StartUpPosition = 3 'Windows Default + Begin VB.CommandButton btnConnect + Caption = "Connect" + Height = 375 + Left = 3480 + TabIndex = 4 + Top = 120 + Width = 1095 + End + Begin VB.CheckBox chkQuiet + Caption = "Quiet" + Height = 300 + Left = 3720 + TabIndex = 8 + Top = 480 + Width = 855 + End + Begin VB.TextBox txtPort + Height = 285 + Left = 2880 + TabIndex = 3 + Top = 120 + Width = 495 + End + Begin VB.CommandButton btnExit + Cancel = -1 'True + Caption = "Exit" + Height = 375 + Left = 3120 + TabIndex = 13 + Top = 2760 + Width = 855 + End + Begin VB.CommandButton btnSend + Caption = "Send" + Height = 375 + Left = 720 + TabIndex = 11 + Top = 2760 + Width = 855 + End + Begin VB.TextBox txtMyName + Height = 285 + Left = 1080 + TabIndex = 6 + Top = 480 + Width = 1215 + End + Begin VB.TextBox txtHostName + Height = 285 + Left = 1080 + TabIndex = 1 + Top = 120 + Width = 1215 + End + Begin VB.CommandButton btnClear + Caption = "Clear" + Height = 375 + Left = 1920 + TabIndex = 12 + Top = 2760 + Width = 855 + End + Begin VB.TextBox txtSend + Height = 495 + Left = 120 + MultiLine = -1 'True + ScrollBars = 2 'Vertical + TabIndex = 10 + Top = 2160 + Width = 4455 + End + Begin VB.TextBox txtRecv + Height = 1215 + Left = 120 + Locked = -1 'True + MultiLine = -1 'True + ScrollBars = 2 'Vertical + TabIndex = 9 + Top = 840 + Width = 4455 + End + Begin VB.CheckBox chkAnonymous + Caption = "Anonymous" + Height = 300 + Left = 2400 + TabIndex = 7 + Top = 480 + Width = 1215 + End + Begin VB.Label lblPort + Caption = "Port" + Height = 255 + Left = 2400 + TabIndex = 2 + Top = 120 + Width = 375 + End + Begin VB.Label lblMyName + Caption = "My Name" + Height = 255 + Left = 120 + TabIndex = 5 + Top = 480 + Width = 855 + End + Begin VB.Label lblHostName + Caption = "Host Name" + Height = 255 + Left = 120 + TabIndex = 0 + Top = 120 + Width = 855 + End +End +Attribute VB_Name = "frmVBChat" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False +Option Explicit + +Private Declare Function lstrlenA Lib "KERNEL32.DLL" (ByVal lpString As Long) As Long + +Private Sub btnConnect_Click() + +Dim lpsz As Long, nPort As Long, df As Double +If nSocket = INVALID_SOCKET Then + df = Val(txtPort.Text) + nPort = IIf(df < 0.5 Or df >= 65535.5, 23, df) + If txtHostName.Text <> vbNullString And nPort > 0 And nPort < 65536 Then + txtPort.Text = LTrim(Str(nPort)) + nSocket = WSBConnect(txtHostName.Text, nPort) + If nSocket = INVALID_SOCKET Then + MsgBox "Connection Failed", vbExclamation + Else + If chkAnonymous.Value = 0 Then + lpsz = CvBStr("[" + txtMyName.Text + " Connected at " + Time$ + "]" + vbCrLf) + WSBSend nSocket, lpsz, lstrlenA(lpsz) + End If + frmVBChat.btnConnect.Caption = "Disconnect" + frmVBChat.btnSend.Enabled = True + End If + End If +Else + If chkAnonymous.Value = 0 Then + lpsz = CvBStr("[" + txtMyName.Text + " Disconnected at " + Time$ + "]" + vbCrLf) + WSBSend nSocket, lpsz, lstrlenA(lpsz) + End If + WSBDisconnect nSocket + nSocket = INVALID_SOCKET + frmVBChat.btnConnect.Caption = "Connect" + frmVBChat.btnSend.Enabled = False +End If + +End Sub + +Private Sub chkAnonymous_Click() + +txtMyName.Enabled = (chkAnonymous.Value = 0) +txtMyName.BackColor = IIf(chkAnonymous.Value = 0, vbWindowBackground, vbButtonFace) + +End Sub + +Private Sub txtSend_KeyDown(nKeyCode As Integer, nShift As Integer) + +If nSocket <> INVALID_SOCKET And nKeyCode = vbKeyReturn And (nShift And vbCtrlMask) <> 0 Then + btnSend_Click + txtSend.Locked = True +End If + +End Sub + +Private Sub txtSend_KeyUp(nKeyCode As Integer, nShift As Integer) + +If txtSend.Locked Then + txtSend.Locked = False +End If + +End Sub + +Private Sub btnSend_Click() + +Dim lpsz As Long +lpsz = CvBStr(IIf(chkAnonymous.Value = 0, "[" + txtMyName.Text + " Sent at " + Time$ + "]" + vbCrLf + txtSend.Text + vbCrLf, txtSend.Text)) +WSBSend nSocket, lpsz, lstrlenA(lpsz) +txtSend.Text = "" + +End Sub + +Private Sub btnClear_Click() + +txtRecv.Text = "" + +End Sub + +Private Sub btnExit_Click() + +Unload Me + +End Sub + +Private Sub Form_Load() + +txtHostName.Text = GetSetting("VBChat", "Client", "HostName", "localhost") +txtPort.Text = GetSetting("VBChat", "Client", "Port", "23") +txtMyName.Text = GetSetting("VBChat", "Client", "MyName", "My Name") +chkAnonymous.Value = IIf(GetSetting("VBChat", "Client", "Anonymous", "0") = "0", 0, 1) +chkQuiet.Value = IIf(GetSetting("VBChat", "Client", "Quiet", "0") = "0", 0, 1) +txtMyName.Enabled = (chkAnonymous.Value = 0) +txtMyName.BackColor = IIf(chkAnonymous.Value = 0, vbWindowBackground, vbButtonFace) +btnSend.Enabled = False + +End Sub + +Private Sub Form_UnLoad(nCancel As Integer) + +Dim lpsz As Long +If nSocket <> INVALID_SOCKET Then + If chkAnonymous.Value = 0 Then + lpsz = CvBStr("[" + txtMyName.Text + " Disconnected at " + Time$ + "]" + vbCrLf) + WSBSend nSocket, lpsz, lstrlenA(lpsz) + End If + WSBDisconnect nSocket + nSocket = INVALID_SOCKET +End If +SaveSetting "VBChat", "Client", "HostName", txtHostName.Text +SaveSetting "VBChat", "Client", "Port", txtPort.Text +SaveSetting "VBChat", "Client", "MyName", txtMyName.Text +SaveSetting "VBChat", "Client", "Anonymous", IIf(chkAnonymous.Value = 0, "0", "1") +SaveSetting "VBChat", "Client", "Quiet", IIf(chkQuiet.Value = 0, "0", "1") +bRunning = False + +End Sub + diff --git a/MISC/VBCHAT/VBCHATC.VBP b/MISC/VBCHAT/VBCHATC.VBP new file mode 100644 index 0000000..dae6e43 --- /dev/null +++ b/MISC/VBCHAT/VBCHATC.VBP @@ -0,0 +1,46 @@ +Type=Exe +Form=VBCHATC.FRM +Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\WINDOWS\system32\stdole2.tlb#OLE Automation +Module=mdlVBChat; VBCHATC.BAS +Module=mdlWinSockBasic; ..\..\utility\WSOCKBAS.BAS +Module=mdlBase; ..\..\utility\BASE.BAS +IconForm="frmVBChat" +Startup="Sub Main" +HelpFile="" +Title="VBChat Client" +ExeName32="VBCHATC.EXE" +Path32="..\..\BIN" +Command32="" +Name="prjVBChatClient" +HelpContextID="0" +CompatibleMode="0" +MajorVer=1 +MinorVer=0 +RevisionVer=0 +AutoIncrementVer=0 +ServerSupportFiles=0 +VersionComments="VBChat Client, (C) 2004-2006 www.elephantbase.net" +VersionCompanyName="Author: Morning Yellow (Address: Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)" +VersionFileDescription="VBChat Client" +VersionLegalCopyright="VBChat Client, (C) 2004-2006 www.elephantbase.net" +VersionLegalTrademarks="www.elephantbase.net" +VersionProductName="VBChat Client" +CompilationType=0 +OptimizationType=0 +FavorPentiumPro(tm)=0 +CodeViewDebugInfo=0 +NoAliasing=0 +BoundsCheck=0 +OverflowCheck=0 +FlPointCheck=0 +FDIVCheck=0 +UnroundedFP=0 +StartMode=0 +Unattended=0 +Retained=0 +ThreadPerObject=0 +MaxNumberOfThreads=1 +DebugStartupOption=0 + +[MS Transaction Server] +AutoRefresh=1 diff --git a/MISC/VBCHAT/VBCHATS.BAS b/MISC/VBCHAT/VBCHATS.BAS new file mode 100644 index 0000000..234ad77 --- /dev/null +++ b/MISC/VBCHAT/VBCHATS.BAS @@ -0,0 +1,93 @@ +Attribute VB_Name = "mdlVBChat" +Option Explicit + +Public Const MAX_CLIENTS As Integer = 256 +Public Const BUFFER_LEN As Long = 65536 + +Public App_sServer As Long, App_sClients(1 To MAX_CLIENTS) As Long, App_nNext As Integer +Public App_bRunning As Boolean +Public App_lpPrevWndFunc As Long + +Public Sub StopServer() + +Dim i As Integer +For i = 1 To MAX_CLIENTS + If App_sClients(i) <> INVALID_SOCKET Then + WSBDisconnect App_sClients(i) + End If +Next +WSBCloseServer App_sServer +App_sServer = INVALID_SOCKET + +End Sub + +Public Function NewWndFunc(ByVal hWnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long + +If msg = WM_TRAY Then + If lParam = WM_LBUTTONUP Then + frmVBChat.WindowState = vbNormal + frmVBChat.Show + ElseIf lParam = WM_RBUTTONUP Then + frmVBChat.PopupMenu frmVBChat.mnTray, , , , frmVBChat.mnTrayOpen + End If +End If +NewWndFunc = CallWindowProcA(App_lpPrevWndFunc, hWnd, msg, wParam, lParam) + +End Function + +Public Sub Main() + +Dim i As Integer, j As Integer, bReceived As Boolean, nLen As Long +Dim uidTrayIcon As Long +Dim ucBuffer(0 To BUFFER_LEN - 1) As Byte + +WSBStartup +App_sServer = INVALID_SOCKET +App_bRunning = True +frmVBChat.Show +Do While App_bRunning + If App_sServer = INVALID_SOCKET Then + bReceived = False + Else + If App_nNext <= MAX_CLIENTS Then + App_sClients(App_nNext) = WSBAccept(App_sServer) + If App_sClients(App_nNext) <> INVALID_SOCKET Then + For App_nNext = 1 To MAX_CLIENTS + If App_sClients(App_nNext) = INVALID_SOCKET Then + Exit For + End If + Next + End If + End If + bReceived = False + For i = 1 To MAX_CLIENTS + If App_sClients(i) <> INVALID_SOCKET Then + nLen = WSBRecv(App_sClients(i), VarPtr(ucBuffer(0)), BUFFER_LEN) + If nLen < 0 Then + WSBDisconnect App_sClients(i) + App_sClients(i) = INVALID_SOCKET + App_nNext = i + ElseIf nLen > 0 Then + For j = 1 To MAX_CLIENTS + If App_sClients(j) <> INVALID_SOCKET Then + If frmVBChat.chkNoEcho.Value = 0 Or j <> i Then + WSBSend App_sClients(j), VarPtr(ucBuffer(0)), nLen + End If + End If + Next + bReceived = True + End If + End If + Next + End If + If Not bReceived Then + Sleep 1 + DoEvents + End If +Loop +If App_sServer <> INVALID_SOCKET Then + StopServer +End If +WSBCleanup + +End Sub diff --git a/MISC/VBCHAT/VBCHATS.FRM b/MISC/VBCHAT/VBCHATS.FRM new file mode 100644 index 0000000..e22016c --- /dev/null +++ b/MISC/VBCHAT/VBCHATS.FRM @@ -0,0 +1,167 @@ +VERSION 5.00 +Begin VB.Form frmVBChat + BorderStyle = 1 'Fixed Single + Caption = "VBChat Server" + ClientHeight = 960 + ClientLeft = 150 + ClientTop = 435 + ClientWidth = 2265 + BeginProperty Font + Name = "宋体" + Size = 9 + Charset = 134 + Weight = 400 + Underline = 0 'False + Italic = 0 'False + Strikethrough = 0 'False + EndProperty + Icon = "VBCHATS.frx":0000 + MaxButton = 0 'False + ScaleHeight = 960 + ScaleWidth = 2265 + StartUpPosition = 3 'Windows Default + Begin VB.CommandButton btnExit + Cancel = -1 'True + Caption = "Exit" + Height = 375 + Left = 1200 + TabIndex = 4 + Top = 480 + Width = 855 + End + Begin VB.CommandButton btnStart + Caption = "Start" + Height = 375 + Left = 240 + TabIndex = 3 + Top = 480 + Width = 855 + End + Begin VB.CheckBox chkNoEcho + Caption = "No Echo" + Height = 300 + Left = 1200 + TabIndex = 2 + Top = 120 + Width = 975 + End + Begin VB.TextBox txtPort + Height = 285 + Left = 600 + TabIndex = 1 + Top = 120 + Width = 495 + End + Begin VB.Label lblPort + Caption = "Port" + Height = 255 + Left = 120 + TabIndex = 0 + Top = 120 + Width = 375 + End + Begin VB.Menu mnTray + Caption = "Tray Menu" + Visible = 0 'False + Begin VB.Menu mnTrayOpen + Caption = "&Open" + End + Begin VB.Menu mnTrayStart + Caption = "&Start" + End + Begin VB.Menu mnTrayExit + Caption = "E&xit" + End + End +End +Attribute VB_Name = "frmVBChat" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False +Option Explicit + +Private Sub mnTrayOpen_Click() + +WindowState = vbNormal +Show + +End Sub + +Private Sub mnTrayStart_Click() + +btnStart_Click + +End Sub + +Private Sub mnTrayExit_Click() + +Unload Me + +End Sub + +Private Sub btnStart_Click() + +Dim i As Integer, nPort As Long +If App_sServer = INVALID_SOCKET Then + nPort = Str2Lng(Val(txtPort.Text), 1, 65535) + txtPort.Text = nPort + App_sServer = WSBOpenServer(nPort) + If App_sServer = INVALID_SOCKET Then + MsgBox "Error Opening Port" + Str(nPort), vbExclamation + Else + For i = 1 To MAX_CLIENTS + App_sClients(i) = INVALID_SOCKET + Next + App_nNext = 1 + txtPort.Enabled = False + chkNoEcho.Enabled = False + btnStart.Caption = "Stop" + mnTrayStart.Caption = "&Stop" + End If +Else + StopServer + txtPort.Enabled = True + chkNoEcho.Enabled = True + btnStart.Caption = "Start" + mnTrayStart.Caption = "&Start" +End If + +End Sub + +Private Sub btnExit_Click() + +Unload Me + +End Sub + +Private Sub Form_Resize() + +If WindowState = vbMinimized Then + AddTrayIcon Me + Hide +Else + DeleteTrayIcon Me +End If + +End Sub + +Private Sub Form_Load() + +txtPort.Text = Str2Lng(GetSetting("VBChat", "Server", "Port", "23"), 1, 65535) +chkNoEcho.Value = IIf(GetSetting("VBChat", "Server", "NoEcho", "0") = "0", 0, 1) +App_lpPrevWndFunc = SetWindowLongA(hWnd, GWL_WNDPROC, AddressOf NewWndFunc) + +End Sub + +Private Sub Form_UnLoad(nCancel As Integer) + +If WindowState = vbMinimized Then + DeleteTrayIcon Me +End If +SetWindowLongA hWnd, GWL_WNDPROC, App_lpPrevWndFunc +SaveSetting "VBChat", "Server", "Port", txtPort.Text +SaveSetting "VBChat", "Server", "NoEcho", IIf(chkNoEcho.Value = 0, "0", "1") +App_bRunning = False + +End Sub diff --git a/MISC/VBCHAT/VBCHATS.FRX b/MISC/VBCHAT/VBCHATS.FRX new file mode 100644 index 0000000..51097c0 Binary files /dev/null and b/MISC/VBCHAT/VBCHATS.FRX differ diff --git a/MISC/VBCHAT/VBCHATS.ICO b/MISC/VBCHAT/VBCHATS.ICO new file mode 100644 index 0000000..6098905 Binary files /dev/null and b/MISC/VBCHAT/VBCHATS.ICO differ diff --git a/MISC/VBCHAT/VBCHATS.VBP b/MISC/VBCHAT/VBCHATS.VBP new file mode 100644 index 0000000..05d6cc4 --- /dev/null +++ b/MISC/VBCHAT/VBCHATS.VBP @@ -0,0 +1,46 @@ +Type=Exe +Form=VBCHATS.FRM +Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\WINDOWS\system32\stdole2.tlb#OLE Automation +Module=mdlVBChat; VBCHATS.BAS +Module=mdlWinSockBasic; ..\..\utility\WSOCKBAS.BAS +Module=mdlBase; ..\..\utility\BASE.BAS +IconForm="frmVBChat" +Startup="Sub Main" +HelpFile="" +Title="VBChat Server" +ExeName32="VBCHATS.EXE" +Path32="..\..\BIN" +Command32="" +Name="prjVBChatServer" +HelpContextID="0" +CompatibleMode="0" +MajorVer=1 +MinorVer=0 +RevisionVer=0 +AutoIncrementVer=0 +ServerSupportFiles=0 +VersionComments="VBChat Server, (C) 2004-2007 www.elephantbase.net" +VersionCompanyName="Author: Morning Yellow (Address: Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)" +VersionFileDescription="VBChat Server" +VersionLegalCopyright="VBChat Server, (C) 2004-2007 www.elephantbase.net" +VersionLegalTrademarks="www.elephantbase.net" +VersionProductName="VBChat Server" +CompilationType=0 +OptimizationType=0 +FavorPentiumPro(tm)=0 +CodeViewDebugInfo=0 +NoAliasing=0 +BoundsCheck=0 +OverflowCheck=0 +FlPointCheck=0 +FDIVCheck=0 +UnroundedFP=0 +StartMode=0 +Unattended=0 +Retained=0 +ThreadPerObject=0 +MaxNumberOfThreads=1 +DebugStartupOption=0 + +[MS Transaction Server] +AutoRefresh=1 diff --git a/PROTOCOL/cchess_ucci.htm b/PROTOCOL/cchess_ucci.htm new file mode 100644 index 0000000..70898ae --- /dev/null +++ b/PROTOCOL/cchess_ucci.htm @@ -0,0 +1,1100 @@ + + + + + +中国象棋电脑应用规范(五):中国象棋通用引擎协议 + + + + +
    +
    +
    中国象棋电脑应用规范(五)
    +
    +
     
    +
    +
    中国象棋通用引擎协议 版本:3.0
    +
    +
     
    +
    +
    黄晨 * 200412月初稿,200711月修订
    +
    +
    ( * 上海计算机博弈研究所,eMailwebmaster@elephantbase.net)
    +
    +
     
    +
    一、概述
    +
     
    +
      中国象棋通用引擎协议(Universal Chinese Chess Protocol,简称UCCI),是一种象棋界面和象棋引擎之间的基于文本的通讯协议。设立中国象棋通用引擎协议的目的有:
    +
      (1) 使一个“可视化象棋软件”可以使用不同的“核心智能部件”,这些核心智能部件称为“引擎”,凡是遵循UCCI的引擎,都可以被该可视化象棋软件(也称为“界面”)所调用;
    +
      (2) 针对所有遵循UCCI的引擎,都可以开发不同的界面,使其具有不同的功能。
    +
      这样,“可视化象棋软件”和“核心智能部件”实现了分离,使得一部分程序设计师能专注于前者(界面)的开发,而另一部分程序设计师能专注于后者(引擎)的开发,让中国象棋软件的设计工作系统化、分工化,提高软件设计效率。
    +
      UCCI是模仿国际象棋的UCI来制定的。UCCI是开放式的协议,并且具有UCI的所有特点,具体反映在象棋百科全书网所收录的以下文章中:
    +
      (1) 国际象棋引擎:穿越困惑(转载自《国际象棋译文苑》)
    +
      (2) 国际象棋引擎协议历史(转载自《国际象棋译文苑》)
    +
      (3) 国际象棋通用引擎协议
    +
     
    +
      UCCI自诞生以来不断在发展和更新,但保持了对早期版本的兼容。
    +
    + +
    +
      3.0版较2.3版改进的内容有:
    +
    + +
      +
    • 建议取消option反馈中的repetitiondrawmoves选项,将selectivity选项改成randomness,增加promotion选项。
    • +
    + +
    +
      2.3版较2.2版改进的内容有:
    +
    + +
      +
    • 建议采用“毫秒”作为唯一的时间单位,参阅option反馈中的usemillisec选项。
    • +
    + +
    +
      以后UCCI还会不定期地更新,并继续保持对早期版本的兼容。UCCI界面和引擎设计者可访问以下资源,来获得最新的UCCI版本:
    +
        http://www.elephantbase.net/protocol/cchess_ucci.htm
    +
     
    +
    二、通讯方法
    +
     
    +
      不管是Windows还是UNIX平台,能被界面调用的引擎都必须是编译过的可执行文件,它跟界面之间通过“标准输入”和“标准输出”(C/C++语言中的stdinstdout)通道来通讯。如果引擎从Windows平台移植到UNIX平台,那么需要重新编译源代码(管道操作的程序也需要作适当修改),或使用跨平台接口。
    +
      作为界面的设计,要启动一个引擎,Windows平台下可用CreateProcess()函数,UNIX平台下可用fork()exec()函数,然后重定向到一个输入管道和一个输出管道,具体操作可参阅WinBoard/XBoard源程序的StartChildProcess()函数,或参阅中国象棋引擎ElephantEye源程序的<pipe.cpp>模块。
    +
      作为引擎的设计,通讯比界面略为简单(只需要对stdinstdout操作),只在检查stdin是否有输入时较为麻烦,具体操作可参阅Crafty源程序的<utility.c>模块的CheckInput()函数,或参阅中国象棋引擎ElephantEye源程序的<pipe.cpp>模块。
    +
     
    +
      通常,界面向引擎发送的信息称为“指令”,而引擎向界面发送的信息称为“反馈”。在UCCI中,不管是指令还是反馈,都是以“行”为单位的,即每条指令和反馈都必须以“回车”(C/C++语言中的'\n')结束。
    +
      注意:引擎用缓冲方式发出反馈(C/C++语言中直接将字符串写入stdout),那么每输出一行都必须用fflush()语句刷新缓冲区。
    +
     
    +
    三、引擎的状态
    +
     
    +
      UCCI引擎在启动后,有三种状态。
    +
     
    +
      (1) 引导状态。
    +
      引擎启动时,即进入引导状态。此时引擎只是等待和捕捉界面的输入,而界面必须用ucci指令让引擎进入接收其他UCCI指令的空闲状态(稍后会提到)。当然,引擎也可以保留使用其他协议的权利,例如引擎允许第一条有效指令是cxboard,这样引擎就转而进入CXBoard状态。
    +
      收到ucci只后,引擎要完成一系列初始化工作,以输出ucciok的反馈作为初始化结束的标志,进入空闲状态。如果引导状态下UCCI引擎收到其他指令,则可以退出。
    +
     
    +
      (2) 空闲状态。
    +
      该状态下引擎没有思考(即几乎不占用CPU资源),而只是等待和捕捉界面的输入(和引导状态类似),接收这样几类指令:A. 设置引擎选项(setoption指令)B. 设置引擎的内置局面(即让引擎思考的局面)及其禁止着法(positionbanmoves指令)C. 让引擎思考(go指令)D. 退出(quit指令)
    +
     
    +
      (3) 思考状态。
    +
      引擎收到go指令后,即进入思考状态,以输出bestmovenobestmove的反馈作为思考状态结束的标志(回到空闲状态)。该状态下引擎将满负荷运转(CPU资源占用率接近100%),但仍旧需要捕捉界面的输入(只有在批处理模式下不会捕捉界面的输入),接收两类指令:A. 中止思考(stop指令)B. 改变思考方式(ponderhit指令)
    +
      go指令只决定了引擎将按照什么思考方式来思考(即限定思考的深度,或限定思考的局面个数,或限定思考的时间),而思考的局面则必须通过前面输入的position指令来告诉引擎。
    +
     
    +
      其他注意事项有:
    +
      (1) 引擎只有在接收到go指令后才开始思考。即便引擎支持后台思考,在输出着法(反馈bestmove)后也不会自动进行,而是要由界面发送go ponder指令,让引擎以后台思考方式进行思考。
    +
      (2) bestmove的反馈并不改变引擎的内置局面,如果界面让引擎走棋,就必须读取bestmove反馈的着法,并在界面的局面上走完这一步(当然,界面也可以走别的着法),再由position指令把新的局面告诉引擎。
    +
      (3) 如果对局是计时的,那么每次思考时都必须用go指令设定时钟,引擎仅仅根据时钟来决定分配多少时间来思考,回到空闲状态后时钟就失效了,必须由界面扣去引擎思考的时间(从发送go指令起到收到bestmove反馈结束),在下次发送go指令时把新的时钟告诉引擎。
    +
      (4) 启用“批处理”模式时,引擎在思考状态下就不接收指令。批处理模式适合用重定向方式调试引擎,例如一个输入文件含有以下指令集:
    +
     
    +
    1: ucci
    +
    2: setoption batch + true
    +
    3: position fen <fen_1>
    +
    4: go depth 10
    +
    5: position fen <fen_2>
    +
    6: go depth 10
    +
    7: quit
    +
     
    +
      第4行以后引擎即进入思考状态,由于处于批处理模式,引擎反馈bestmove后回到空闲状态,才会继续接收以后的指令。如果没有第2行的启用批处理模式,那么第4行以后的指令都将在思考状态接收,而对于思考状态,这些指令都是无效的。
    +
      (5) 如果界面搞错了引擎的状态,在引擎的思考状态向界面发送quit指令,那么引擎最好能终止思考并立即退出,以避免界面无休止地等待引擎的退出。
    +
      (6) 如果界面搞错了引擎的状态,在引擎的空闲状态向引擎发送stop指令,那么引擎最好能反馈一个nobestmove,以避免界面无休止地等待引擎的反馈。
    +
     
    +
    四、着法和棋盘的表示
    +
     
    +
      界面告诉引擎哪些着法是禁手(banmoves指令),或者引擎回答界面应该走哪个着法(bestmove反馈),这样的着法都用4个字符(简化的ICCS格式,参阅《中国象棋电脑应用规范():着法表示》一文)表示,即ICCS格式去掉中间的横线,并改成小写,例如h2e2
    +
      界面用position指令把局面告诉引擎时,应该使用FEN(写法参阅《中国象棋电脑应用规范()FEN文件格式》一文)。但是对局中会遇到循环局面,引擎也必须考虑其对策,因此FEN串并不能完全反映局面信息,必须使用FEN(当前局面前第一个不吃子的局面)和后续着法相结合的方法表示局面。例如,开局以后走了以下4步:
    +
      + + + + + + + + + +
    1. 炮二平五 炮8平5 2. 炮五进四 士4进5
    +
    +
    + +
    +
      如果把这4步棋涉及的5个局面都告诉引擎,那么指令依次是:
    +
     
    +
    1: position fen + rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR + w - - 0 1
    +
    2: position fen + rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR + w - - 0 1 moves h2e2
    +
    3: position fen + rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR + w - - 0 1 moves h2e2 h7e7
    +
    4: position fen + rnbakabnr/9/1c2c4/p1p1C1p1p/9/9/P1P1P1P1P/1C7/9/RNBAKABNR + b - - 0 2
    +
    5: position fen + rnbakabnr/9/1c2c4/p1p1C1p1p/9/9/P1P1P1P1P/1C7/9/RNBAKABNR + b - - 0 2 moves d9e8
    +
     
    +
      其中第4行更换了FEN串,因为该局面前一个着法是吃子着法。
    +
     
    +
    五、指令和反馈
    +
     
    +
      按照惯例,指令用红色表示,反馈用蓝色表示。
    +
     
    +
    1. ucci
    +
      引导状态的指令。这是引擎启动后,界面需要给引擎发送的第一条指令,通知引擎现在使用的协议是UCCI
    +
     
    +
    2. id {name | copyright | author | user} + <信息>
    +
      引导状态的反馈。显示引擎的版本号、版权、作者和授权用户,例如: +
    +
    id name + ElephantEye 1.6 Beta,说明引擎的版本号是ElephantEye 1.6 Beta
    +
    id copyright + 2004-2006 www.elephantbase.net,说明引擎的版权属于www.elephantbase.net所有;
    +
    id author + Morning Yellow,说明引擎的作者是Morning Yellow
    +
    id user + ElephantEye Test Team,说明引擎授权给用户ElephantEye Test Team使用。
    +
     
    +
    3. option <选项> type <类型> [min <最小值>] [max <最大值>] [var <可选项> [var <可选项> [...]]] [default + <默认值>]
    +
      引导状态的反馈。显示引擎所支持的选项,<option>指选项的名称(后面会介绍),选项的类型是label(标签,非选项)button(指令)check(是或非)combo(多选项)spin(整数)string(字符串)中的一种。
    +
      通常的UCCI引擎支持以下选项:
    +
      (1) usemillisec(check),通知界面采用毫秒模式。建议引擎始终采用毫秒模式(go指令的时间单位是“毫秒”),并总是在ucciok前输出option usemillisec + ... 的反馈信息。除非引擎不发送 option usemillisec + ... 的反馈信息,否则界面将自动使用毫秒模式,并向引擎发送 setoption usemillisec true 的指令。目前已知的UCCI界面程序(如象棋巫师、UCCI引擎联赛模拟器等)都采用这种做法;
    +
      (2) batch(check),批处理模式(前面介绍过),默认是关闭的;
    +
      (3) debug(check),调试模式,默认是关闭的,打开后引擎会输出更多的信息(info反馈),以帮助调试;
    +
      (4) ponder(check),是否使用后台思考的时间策略,默认是关闭的,设定该参数的目的仅仅是让引擎改变时间分配策略,而后台思考则仍然需要界面发出指令,参阅go ponderponderhit指令;
    +
      (5) usebook(check),是否使用开局库的着法,默认是启用的,如果关闭的话,即便当前局面在开局库中有着法,引擎也会不顾开局库而思考的;
    +
      (6) useegtb(check),是否使用残局库,默认是启用的,和usebook类似;
    +
      (7) bookfiles(string),设定开局库文件的名称,可指定多个开局库文件,用分号“;”隔开,如不让引擎使用开局库,除了可以关闭usebook选项外,还可以把bookfiles设成空值;
    +
      (8) egtbpaths(string),设定残局库路径的名称,和bookfiles类似;
    +
      (9) evalapi(string),设定局面评价API函数库文件的名称,和bookfiles类似,但只能是一个文件(例如,Windows下默认值是EVALUATE.DLLLinux下默认值是libeval.so)
    +
      (10) hashsize(spin),以MB为单位规定Hash表的大小,0表示让引擎自动分配Hash表;
    +
      (11) threads(spin),支持多处理器并行运算(SMP)的引擎可指定线程数(即最多可运行在多少处理器上)0表示让引擎自动分配线程数;
    +
      (12) idle(combo),设定处理器的空闲状态,通常有none(满负荷)small(高负荷)medium(中负荷)large(低符合)四种选项,引擎默认总是以满负荷状态运行的,而设置比较大的空闲状态,可以在人机对弈时留出适当的处理器资源,让用户运行其他程序;
    +
      (13) promotion(check),是否允许仕()()升变成兵(),这是一种中国象棋的改良玩法,默认是不允许的(即默认采用常规走法)
    +
      (14) pruning(combo),设定裁剪程度,裁剪越多则引擎的搜索速度越快,但搜索结果不准确的可能性越大,通常有none()small()medium()large()四种,一般都设为large以充分展示引擎的搜索速度,但在处理一些刁难性的排局时,用largemedium不一定能解出,可尝试smallnone
    +
      (15) knowledge(combo),设定知识大小,通常知识量越多则程序的静态局面评价越准确,但的运算速度会变慢,该选项和pruning一样有四种设定,一般都使用large,但在解杀局时不需要静态局面评价,可以把知识量设置得小些;
    +
      (16) randomness(combo),设定随机性系数,和pruning一样有四种设定,一般都设为none,以保证引擎走出它认为最好的着法,但为了增强走棋的趣味性,可以把这个参数调高,允许引擎走出它认为不是最好的着法,以丰富走棋的样式;
    +
      (17) style(combo),设定下棋的风格,通常有solid(保守)normal(均衡)risky(冒进)三种;
    +
      (18) newgame(button),设置新局或新的局面,引擎收到该指令时,可以执行导入开局库、清空Hash表等操作,UCCI界面《象棋巫师》在每次新建棋局或重新编辑局面时都会发送 setoption newgame 这条指令。
    +
      需要注意的是,各种引擎提供的选项内容是不一样的,也并不是所有的UCCI界面支持这些选项的,例如目前的UCCI界面《象棋巫师》没有对batchdebug等选项的设置(它们只供调试时使用)
    +
     
    +
    4. ucciok
    +
      引导状态的反馈,此后引擎进入空闲状态。
    +
     
    +
    5. isready
    +
      空闲状态和思考状态的指令。检测引擎是否处于就绪状态,其反馈总是readyok,该指令仅仅用来检测引擎是否能够正常接收指令。
    +
     
    +
    6. readyok
    +
      空闲状态和思考状态的反馈。表明引擎处于就绪状态(可正常接收指令)
    +
     
    +
    7. setoption <选项> [<>]
    +
      空闲状态的指令。设置引擎参数,这些参数都应该是option反馈的参数,例如: +
    +
     
    +
    setoption + usebook false,不让引擎使用开局库;
    +
    setoption + selectivity large,把选择性设成最大;
    +
    setoption + style risky,指定冒进的走棋风格;
    +
    setoption + loadbook,初始化开局库。
    +
     
    +
      但是,设置option反馈没有给出的参数,并不会出错。例如UCCI界面《象棋巫师》就从不识别option反馈,而直接根据用户的设置发送setoption指令。
    +
     
    +
    8. position {fen <FEN> | startpos} [moves + <后续着法列表>]
    +
      空闲状态的指令。设置“内置棋盘”的局面,用fen来指定FEN格式串,moves后面跟的是随后走过的着法,例如:
    +
    position fen + rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR + w - - 0 1 moves h2e2 h9g7
    +
      FEN格式串的写法参阅《中国象棋电脑应用规范()FEN文件格式》一文。
    +
      moves选项是为了防止引擎着出长打着法而设的,UCCI界面传递局面时,通常fen选项为最后一个吃过子的局面(或开始局面),然后moves选项列出该局面到当前局面的所有着法。
    +
      startpos表示开始局面,它等价于 fen rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR + w - - 0 1
    +
     
    +
    9. banmoves <禁止着法列表>
    +
      空闲状态的指令。为当前局面设置禁手,以解决引擎无法处理的长打问题。当出现长打局面时,棋手可以操控界面向引擎发出禁手指令。例如:
    +
    position fen + 1r2kab1r/2c1a4/n1c1b1n2/4p2N1/p1p6/1C4P2/P1P1P4/2N1B3C/4A4/1RBAK2R1 + w - - 0 1 moves h6i4 i9h9 i4h6 h9i9
    +
    banmoves h6i4
    +
      本例取自《象棋竞赛规则(1999年版)棋例图三,由于大多数象棋引擎无法识别红方这种方式的长捉,所以在采用中国象棋协会的比赛规则时,遇到这种局面就必须给引擎发出禁手指令。下一次发送position指令后,前面设置过的禁止着法就取消了,需要重新设置禁止着法。
    +
      目前UCCI界面《象棋巫师》不识别长打禁手,所以不会向引擎发送banmoves指令。
    +
     
    +
    10. go [ponder | draw] <思考模式>
    +
      空闲状态的指令,此后引擎进入思考状态。让引擎根据position指令设定的棋盘来思考,各选项为思考方式,有三种模式可供选择:
    +
      (1) depth <深度> | infinite:限定搜索深度,infinite表示无限制思考(直到找到杀棋或用stop指令中止)。如果深度设定为0,那么引擎可以只列出当前局面静态评价的分数,并且反馈nobestmove
    +
      (2) nodes <结点数>:限定搜索结点数。
    +
      (3) time <时间> [movestogo <剩余步数> | increment <每步加时>] [opptime <对方时间> [oppmovestogo <对方剩余步数> | + oppincrement <对方每步加时>]]:限定时间,时间单位是秒(默认)或毫秒(启用毫秒制时)movestogo适用于时段制,increment适用于加时制。opptimeoppmovestogooppincrement可以让界面把对方的用时情况告诉引擎。
    +
      如果指定ponder选项,则引擎思考时时钟不走,直到接受到ponderhit指令后才计时,该选项用于后台思考,它只对限定时间的思考模式有效。
    +
      指定draw选项表示向引擎提和,引擎以bestmove提供的选项作为反馈,参阅bestmove指令。
    +
      注意:ponderdraw选项不能同时使用,如果界面向正在后台思考中的引擎求和,则使用ponderhit draw指令。
    +
     
    +
    11. info <思考信息>
    +
      思考状态的反馈。显示引擎思考信息,通常有以下几种信息:
    +
      (1) time <已花费的时间> nodes <已搜索的结点数>:思考信息中给出的时间通常以毫秒为单位,结点数和时间相除就是引擎的速度(NPS),单位是K
    +
      (2) depth <当前搜索深度> [score <分值> pv <主要变例>]:输出引擎思考到的深度及其思考路线和好坏。例如在起始局面下,《象棋巫师》收到引擎的反馈:info depth 6 score + 4 pv b0c2 b9c7 c3c4 h9i7 c2d4 h7e7,那么界面上应该输出:6 (+4) 马八进七 马2进3 + 兵七进一 马8进9 马七进六 炮8平5。分值通常以一个轻子(马或炮)100分记,以上信息说明此时当前要走的一方占有相当于0.04个轻子的优势。
    +
      (3) currmove <当前搜索着法>:输出引擎正在思考的着法。
    +
      (4) message <提示信息>:输出引擎要直接告诉用户的信息,建议界面程序直接将提示信息显示在界面上。
    +
     
    +
    12. ponderhit [draw]
    +
      思考状态的指令。告诉引擎后台思考命中,现在转入正常思考模式(引擎继续处于思考状态,此时go指令设定的时限开始起作用)
    +
      指定draw选项表示向引擎提和,引擎以bestmove提供的选项作为反馈,参阅bestmove指令。
    +
     
    +
    13. stop
    +
      思考状态的指令。中止引擎的思考。另外,后台思考没有命中时,就用该指令来中止思考,然后重新输入局面。
    +
      注意:发出该指令并不意味着引擎将立即回到空闲状态,而是要等到引擎反馈bestmovenobestmove后才表示回到空闲状态,引擎应尽可能快地作出这样的反馈。
    +
     
    +
    14. bestmove <最佳着法> [ponder <后台思考的猜测着法>] [draw | resign]
    +
      思考状态的反馈,此后引擎返回空闲状态。显示思考结果,即引擎认为在当前局面下的最佳着法,以及猜测在这个着法后对手会有怎样的应对(即后台思考的猜测着法)。通常,最佳着法是思考路线(主要变例)中的第一个着法,而后台思考的猜测着法则是第二个着法。
    +
      在对手尚未落子时,可以根据该着法来设定局面,并作后台思考。当对手走出的着法和后台思考的猜测着法吻合时,称为“后台思考命中”。
    +
      draw选项表示引擎提和或者接受界面向引擎发送的提和请求,参阅go drawponderhit draw指令。resign选项表示引擎认输。UCCI界面在人机对弈方式下,根据不同情况,可以对引擎的bestmove反馈中的drawresign选项作出相应的处理:
    +
      (1) 如果用户提和,界面向引擎发出go drawponderhit draw指令,而引擎反馈带drawbestmove,那么界面可终止对局并判议和;
    +
      (2) 如果用户没有提和,而引擎反馈带drawbestmove,那么界面可向用户提和,用户接受提和则可终止对局并判议和;
    +
      (3) 如果引擎反馈带resignbestmove,那么界面可终止对局并判引擎认输。
    +
      引擎应该根据当前局面的情况(position指令给出),以及界面是否发送了带drawgoponderhit指令,来考虑是否反馈带drawresignbestmove
    +
     
    +
    15. nobestmove
    +
      思考状态的反馈,此后引擎返回空闲状态。显示思考结果,但引擎一步着法也没计算,表示当前局面是死局面,或者接收到诸如 go depth 0 等只让引擎给出静态局面评价的指令。
    +
     
    +
    16. probe {fen <FEN> | startpos} [moves + <后续着法列表>]
    +
      空闲状态和思考状态的指令。获取Hash表中指定局面的信息,引擎必须立刻在Hash表中查找该局面的状态,由pophash指令反馈。
    +
      该指令仅用于引擎的调试,设计者可以向引擎发送一系列probe指令,捕获到搜索树的大致信息。
    +
     
    +
    17. pophash [bestmove <最佳着法>] [lowerbound <Beta> depth <深度>] [upperbound <Alpha> depth <深度>]
    +
      空闲状态和思考状态的反馈。输出由probe所指定的局面在Hash表中信息。
    +
      如果该局面没有记录在Hash表中,那么只反馈pophash即可。
    +
     
    +
    18. quit
    +
      空闲状态的指令。让引擎退出运转。
    +
     
    +
    19. bye
    +
      接收到quit指令后的反馈。引擎完成了退出运转前的准备工作,通知界面,引擎将在瞬间正常退出运转。界面收到该指令后,即可关闭输入输出通道。
    +
     
    +
    六、用例
    +
     
    +
      下面是一个后台思考的例子,描述了UCCI引擎中最难处理的部分。(从界面到引擎的)指令用红色表示,(从引擎到界面的)反馈用蓝色表示。
    +
     
    +
    ucci
    +
    id name + ElephantEye Demo
    +
    option + usemillisec type check default false
    +
    option + usebook type check default true
    +
    ucciok
    +
    setoption + usemillisec true
    +
    setoption + usebook false
    +
    position fen + rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR + w - - 0 1
    +
    go time + 300000 increment 0
    +
    info depth 6 + score 4 pv b0c2 b9c7 c3c4 h9i7 c2d4 h7e7
    +
    info nodes + 5000000 time 5000
    +
    bestmove b0c2 + ponder b9c7
    +
     
    +
      在这个例子中,引擎执红,用户执黑,采用5分钟包干的时限。引擎启动后,界面即让引擎分析初始局面(不用开局库),引擎给出最佳着法“马八进七”,然后是用户走子,引擎按照猜测着法“马2进3”作后台思考。此时,引擎消耗了5秒钟,还剩余295秒的时间。
    +
     
    +
    position fen + rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR + w - - 0 1 moves b0c2 b9c7
    +
    go ponder + time 295000 increment 0
    +
    info depth 6 + score 4 pv c3c4 h9i7 c2d4 h7e7 h0g2 i9h9
    +
     
    +
      此时用户走子了,下面分两种情况讨论。
    +
      (1) 如果用户走了引擎的猜测着法“马2进3”,那么后台思考命中:
    +
     
    +
    ponderhit
    +
    info nodes + 10000000 time 10000
    +
    info depth 7 + score 4 pv c3c4 h9i7 c2d4 h7e7 h0g2 i9h9 i0h0
    +
    info nodes + 15000000 time 15000
    +
    bestmove c3c4 + ponder h9i7
    +
     
    +
      现在引擎走“兵七进一”,并且猜测对方会走“马8进9”。尽管这着棋引擎思考了15秒钟,但是前10秒钟用的是对手的时间,自己的时间是从ponderhitbestmove的部分,所以自己的时间只消耗了5秒钟,因此还剩余290秒的时间。然后引擎继续后台思考。
    +
     
    +
    position fen + rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR + w - - 0 1 moves b0c2 b9c7 c3c4 h9i7
    +
    go ponder + time 290000 increment 0
    +
    ……
    +
     
    +
      (2) 如果用户走了其他的着法(比如“卒3进1”),那么后台思考没有命中,必须根据这个着法重新思考。
    +
    stop
    +
    info nodes + 10000000 time 10000
    +
    bestmove c3c4 + ponder h9i7
    +
    position fen + rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR + w - - 0 1 moves b0c2 c6c5
    +
    go time + 295000 increment 0
    +
    ……
    +
     
    +
      如希望获得更详细的关于UCCI操作上的细节,可参考UCCI引擎ElephantEye的源程序及其说明材料,这里不再作过多的介绍。
    +
     
    +
    七、电脑象棋联赛
    +
     
    +
      电脑象棋联赛使用UCCI引擎,但是参赛引擎并不一定要支持UCCI的全部内容,只需要能跟“UCCI引擎联赛模拟器”正常通讯就可正常比赛了,模拟器相当于一个支持UCCI的界面。参赛引擎必须能够识别并正确处理的指令有:
    +
      (1) ucci
    +
      (2) position fen ... [moves + ...]
    +
      (3) banmoves ...
    +
      (4) go [draw] time ... + increment ... [opptime ... oppincrement ...]
    +
      (5) quit
    +
      参赛引擎必须能够反馈的信息有:
    +
      (1) ucciok
    +
      (2) bestmove ... [draw + | resign]
    +
      为了更好地让引擎适应模拟器,引擎最好能够实现以下功能:
    +
      (1) 支持毫秒制。即启动时有option usemillisec的反馈,能够识别并处理setoption + usemillisec true的指令。启用毫秒制以后,在时间非常紧缺的情况下,模拟器就会准确地把时间告诉引擎,否则只会粗略地给出秒数。
    +
      (2) 支持认输和提和,即当引擎觉得没有机会获胜时,可以用bestmove ... draw提和或接受提和,当引擎觉得没有能力抵抗时,可以用bestmove ... + resign认输,节约比赛时间。
    +
      (3) 支持stop指令。当引擎超时后,模拟器会发送stop指令让引擎立即给出着法(立即反馈bestmove),超过一定时间(如超过0.2)才判超时负。
    +
      另外,识别setoption指令不是必须的,但在联赛中也会有用。例如,如果引擎需要用setoption + bookfiles ...来导入开局库,而模拟器不会自动向引擎发送这条指令,那么引擎必须建立配置文件,其中有setoption + bookfiles ...这行指令,模拟器在启动引擎后,会把配置文件中的每行都作为指令发送给引擎的。
    +
     
    +
    八、和UCI的区别
    +
     
    +
      UCCI是从国际象棋通用引擎协议UCI移植过来的,沿用了大部分UCI的指令和反馈,但是为了适应中国象棋软件的需要,作了以下几点改动:
    +
      (1) 增加了banmoves指令,因为中国象棋有长打作负的规则。
    +
      (2) UCI的反馈option name <选项> type <类型> ...简化为option <选项> type <类型> ...。例如,UCI中有option name Hash + Size type spin这条反馈,而在UCCI中则是option hashsize + type spin。这里去掉了name关键字,因为它总是跟在option后面,显得多此一举。另外,UCI允许选项由多个有大小写的单词组成(Hash Size),这是因为UCI界面会直接把这个选项名称显示在对话框上。而UCCI中的选项只用一个全部由小写字母组成的单词,因为中国象棋的UCCI界面使用中文,因此界面上不能输出Hash Size,而用“置换表”或其他可以看得懂的中文术语。这样,界面还不如识别一个更简单的hashsize(一个全部由小写字母组成的单词),再翻译成“置换表”。
    +
      (3) UCI的指令setoption name + <选项> [value <>]简化为setoption <选项> [<>]。由于UCCI的选项是一个单词,所以第二个空格后的内容肯定是选项的值,因此namevalue就显得多此一举了。
    +
      (4) UCCI明确了引擎所处的三种状态,以及这三种状态下适用的指令和反馈,为界面和引擎的程序设计提供了清晰的思路。
    +
      (5) UCCIgo time <时间> opptime <时间>的形式把双方的时间信息传达给引擎,而不是UCIgo wtime <时间> btime <时间>,这样可以简化引擎解析时间的操作,如果引擎在指定用时策略时不考虑对方的用时,那么可以不理会opptime <时间>
    +
      (6) UCCI明确了4种思考模式,使得指令的解析简单化了。
    +
      (7) UCCI规定position fen ... [moves + ...]指令中的FEN串是当前局面前第一个不吃子的局面,后面再跟该局面的后续着法。而UCI则是用position startpos + moves ...指令,把棋局从头到尾的着法全都列出来,会增加通讯通道的压力。
    +
    + + +
  • 上一篇 中国象棋电脑应用规范()PGN文件格式
  • +
  • 下一篇 象棋软件发布公告——象棋巫师
  • +
  • 返 回 象棋百科全书——规则与协议
  • +
    +
    + + + + + + + + +

    +
    www.elephantbase.net
    +
    + + diff --git a/PROTOCOL/online.gif b/PROTOCOL/online.gif new file mode 100644 index 0000000..416696b Binary files /dev/null and b/PROTOCOL/online.gif differ diff --git a/PROTOCOL/plugin_spec.html b/PROTOCOL/plugin_spec.html new file mode 100644 index 0000000..e540afa --- /dev/null +++ b/PROTOCOL/plugin_spec.html @@ -0,0 +1,468 @@ + + + + + + +Qianhong Xiangqi Plugin Spec + + + + + + + + +

    Qianhong Xiangqi  Plugin Specification

    +QHPLUGIN V1.3
    +Jeremy Craner
    +2004/07/03
    + +www.jcraner.com/qianhong/
    +
      + +

    Plugin Specification + +

    Want to write an AI engine to use with Qianhong? It's not too difficult if you have +some programming experience and a Windows/DOS compiler. + +

    It turns out that, with the addition of plugin capability, Qianhong is sort of like +a WinBoard for Chinese Chess. I hadn't heard of WinBoard when I made the plugins, but +the structure is pretty similar: a GUI that uses piped I/O to communicate with AI +engines. Maybe Qianhong will grow into a Xiangqi WinBoard... who knows. From a glance +at the  WinBoard site, +though, it appears that WinBoard has been around a long time and has a huge following. + +

    The following pages describe the Qianhong plugin protocol: + +

    +
    +

    1. Plugin Overview + +

    A Qianhong AI plugin is simply a Windows console program that is capable of reading +standard input and writing standard output. Qianhong runs the plugin executable as a +detached process with its standard I/O redirected to anonymous pipes. A text-based +protocol, which consists of only a handful of simple commands, is used to communicate +with the plugin. + +

    As an example, the following text shows the first part of a game against QHPlugin.exe +(the default Qianhong AI engine) on a level 3 setting: + +

    LEVEL 3                  (Qianhong sets the AI level)
    +OK - Set AI level to 3   (Plugin accepts it)
    +PLAY H2-E2               (Qianhong plays a move: round 1, red)
    +OK                       (Plugin accepts it)
    +AI                       (Qianhong asks for AI to move)
    +B9-C7                    (Plugin plays a move: round 1, black)
    +PLAY E2-E6               (Qianhong plays a move: round 2, red)
    +OK                       (Plugin accepts it)
    +AI                       (Qianhong asks for AI to move)
    +C7-E6                    (Plugin plays a move: round 2, black)
    +UNDO                     (Qianhong backs out last move)
    +OK                       (Plugin accepts it)
    + +

    That's the basic interface. Now for the details: +2. Plugin Modes. + +


    + +

    2. Plugin Modes + +

    Plugins are run in one of two modes, as specified by a command-line parameter: +"-info" and "-plugin". The info mode is used during Qianhong startup. +It allows Qianhong to query the plugin for a description of its capabilities. +The plugin mode is used to actually play a game of Xiangqi. + +

    Info Mode + +

    When the plugin is run with a "-info" argument, all it has to do is print a standard +list of information and exit. The information is of the format: +

    Protocol Version
    +AI Engine Name
    +LEVELS <N>
    +(N lines of levels)
    +UNDO <0/1>
    +HINTS <0/1>
    +RULES <0/1>
    +BGTHINK <0/1>
    +TIMEOUT <0/1>
    +(Additional lines of Info)
    +ENDINFO
    + +Take the info from QHPlugin.exe as an example (you can see +this yourself by running "<Qianhong dir>\Plugins\Qianhong\QHPlugin.exe -info"): + +
    C:\Qianhong\Plugins\Qianhong>qhplugin.exe -info
    +QHPLUGIN V1.3
    +Qianhong
    +LEVELS 3
    +1 - Very Easy
    +2 - Easy
    +3 - Smarter
    +UNDO 1
    +HINTS 1
    +RULES 1
    +BGTHINK 0
    +TIMEOUT 0
    +Qianhong AI engine for Qianhong v3.1
    +By Jeremy Craner, 2001-2004
    +ENDINFO
    + + +

    Protocol Version The first line is the plugin protocol version. It should read "QHPLUGIN V1.3" (though V1.2 is still supported). The version number allows for future expansion. + +

    AI Engine Name The second line gives the AI engine name that appears in the game. +You'll want to keep it short because the player name boxes are not very big... + +

    Levels The third line tells how many different AI levels the plugin has. +The subsequent lines list each level, one per line, with the number and any additional +text you want to use for description. Levels must be designated by a number. The extra +text (preceded by a space) is optional. + +

    Undo This tells Qianhong whether the UNDO command is supported. +Use 0 for no, 1 for yes. + +

    Hints This tells Qianhong whether the HINTS command is supported. +Use 0 for no, 1 for yes. + +

    Rules This tells Qianhong whether the BAN command is supported. +Use 0 for no, 1 for yes. + +

    BGThink This tells Qianhong whether the BGTHINK command is supported. +Use 0 for no, 1 for yes. + +

    Timeout This tells Qianhong whether the TIMEOUT command is supported. +Use 0 for no, 1 for yes. + +

    Additional Info Following the hints line, all lines up to the ENDINFO line are +treated as additional info that appears in the "Choose AI Engine" dialog box. Blank lines +may be used for formatting. + +

    Plugin Mode + +

    When the plugin is run with a "-plugin [debug]" argument, it is to play a game. +Qianhong redirects STDIN and STDOUT to pipes so it can communicate with the plugin. +The plugin reads commands from STDIN, takes appropriate action, and writes responses +to STDOUT. All commands (except one, but you'll see that later) require a response. +The plugin must flush STDOUT each time it finishes writing a response so that the +data will be sent through the pipe. Failure to flush the stream will cause Qianhong +to wait indefinitely for the data. + +

    The optional "debug" argument is used when Qianhong is run in plugin debugging mode. +(Run Qianhong with "plugin_debug" as the first argument.) In this mode, the plugin will +have a console and may output anything it wants to the console by writing to STDERR. +Of course, you can also write to a file (or something else) if you like. The debug mode +is for your own benefit; use it as you see fit. + +

    The next section, 3. Commands, describes the +commands and their associated responses. + +


    +

    3. Plugin Commands + +

    Qianhong sends the following commands to the plugins. +Each command is discussed in detail below. Command and response words are +in ALL CAPS, though your plugin may choose to recognize lower-case commands as well. + +

      Required:
    +    LEVEL [new-level]
    +    FEN <FEN-string>
    +    PLAY <ICCS-move>
    +    LOAD <count> ...
    +    AI
    +    ABORT
    +    QUIT
    +
    +  Optional:
    +    UNDO
    +    HINTS
    +    BAN <count> ...
    +    BGTHINK <ON|OFF>
    +    TIMEOUT
    + +

    A note on responses: All responses must end with a newline character ('\n'). +Expected responses vary by command, but the two most common responses are OK and ERROR. +Both of these may be followed (on the same line) by additional text as desired. +Errors will usually be reported in a Qianhong message box, in which case the +user will be able to read the text description of the error. + +

    LEVEL
    + + + + +
    CommandLEVEL
    Response<current-level>
    DescriptionFor a LEVEL command with no parameter, reply with the +current level number.
    + + + +
    CommandLEVEL <new-level>
    ResponsesOK [text]
    ERROR [text]
    DescriptionFor a LEVEL command with a parameter, set the current level +to the one specified by the given number. Reply OK if successful, or ERROR if the +number is out of range.
    + +

    FEN
    + + + + +
    CommandFEN <FEN-string>
    ResponsesOK [text]
    ERROR [text]
    DescriptionThis command instructs the plugin to set the board using the +FEN string (see + www.nchess.com/fen.html for details on FEN for Chinese Chess). +Red will always be on the bottom (i.e. the last line of the string). +Reply OK if the board is set, or ERROR if something goes wrong.
    + +

    PLAY
    + + + + +
    CommandPLAY <ICCS-move>
    ResponsesOK [text]
    ERROR [text]
    DescriptionThis tells the plugin to play the given move. The move +parameter is in ICCS notation of the form "A0-A1". Looking down on a board with red +on the bottom, A0 is the lower-left corner (where red's left chariot starts) +and I9 is the upper-right corner (where black's left chariot starts). +So the move "A0-A1" would move the piece in the near corner of red's ninth file +up one spot. (See notation conventions in 4. Tricky Stuff for more.) +Reply OK if the move is made, or ERROR if something goes wrong.
    + +

    LOAD
    + + + + +
    CommandLOAD <count> \n [ICCS-move \n] ...
    ResponsesOK [text]
    ERROR [text]
    DescriptionTells the plugin to play a list of moves. This allows Qianhong to load a whole sequence of moves much quicker than using multiple PLAY commands. The parameter is the number of moves, which are given in ICCS format, one-per-line, following the LOAD command. +
    + +

    AI
    + + + + +
    CommandAI
    Responses<ICCS-move>
    ERROR [text]
    ABORTED
    DescriptionTells the plugin to move for the current player and report +the move. The plugin responds with the move in ICCS notation of the form "A0-A1", or +ERROR if something goes wrong. If the plugin's AI engine takes more than a second to +respond, the plugin must keep watching STDIN for new commands, and interrupt +the AI engine if a command comes before the engine is done thinking. In this case, +the plugin should respond with ABORTED, then process the next command as normal. +

    +Qianhong typically only aborts the AI command when the user closes the app or +quits the current game. The exception is with the TIMEOUT command, which, if supported, is sent to cause the AI to stop thinking and reply with the best move it has come up with so far.
    + +

    ABORT
    + + + + +
    CommandABORT
    Response(none)
    DescriptionThe ABORT command is used when an AI or HINTS command is +in progress and Qianhong needs to cancel it and get the plugin back to a ready state. +Since the plugin must abort AI and HINTS commands when any new command is sent, +this is just a way to stop the AI engine without causing a new command to execute. +The plugin does not reply to the ABORT command directly; the only response is from +a terminated AI or HINTS command that responds with ABORTED. +
    + +

    QUIT
    + + + + +
    CommandQUIT
    ResponseBYE
    DescriptionThis command tells the plugin to stop running and exit. +The plugin responds with BYE when it shuts down. (If the plugin doesn't respond or +close, Qianhong will give it about a second and then kill it. This is a last resort +that may leave resources open, so the plugin should be sure to shut down gracefully +when it is told to quit.) +
    + +

    UNDO
    + + + + +
    CommandUNDO
    ResponsesOK [text]
    ERROR [text]
    DescriptionTells the plugin to undo the last move. The plugin +responds with OK if it can undo it, or ERROR if something goes wrong (or if no moves +have been played yet).

    +The plugin may choose not to implement this command (to the dismay of users =) +by specifying "UNDO 0" in the info mode.
    + +

    HINTS
    + + + + +
    CommandHINTS
    Responses +<hint [description]> \n [hint [description] \n] ... ENDHINTS
    +ERROR [text]
    ABORTED
    DescriptionRequests hints on the current move from the plugin. +This is basically the same as the AI command except that: (1) the plugin does not play +a move, (2) the plugin may suggest more than one move, and (3) the plugin may describe +the move(s) to the user.

    +The nominal response is one or more ICCS moves, one per line, followed by a line that +says ENDHINTS. Moves should be listed in order from best to worst; illegal moves must +not be listed. Each ICCS move may be followed (on the same line) by additional text +describing the move. Other responses are ERROR and ABORTED. (Since this command may take +more than a second to execute, it should be interruptible by ABORT and TIMEOUT--see the AI command for more.)

    +The plugin may choose not to implement this command (to the dismay of users =) +by specifying "HINTS 0" in the info mode.
    + +

    BAN
    + + + + +
    CommandBAN <count> \n [ICCS-move \n] ...
    ResponsesOK [text]
    ERROR [text]
    DescriptionTells the plugin that the listed moves are considered +illegal for the next AI or HINTS command. This allows Qianhong to moderate the game like +a referee, and lets the plugins choose alternate moves accordingly. The parameter is the number of banned moves, which are given in ICCS format, one-per-line, following the BAN command. +

    +The plugin may choose not to implement this command by specifying "RULES 0" in the info mode. In this case, if the plugin plays a banned move, Qianhong will call the game a forfeit. (Forfeits are not pretty, so plugins should support this command if at all possible.)
    + +

    BGTHINK
    + + + + +
    CommandBGTHINK <ON|OFF>
    ResponsesOK [text]
    ERROR [text]
    DescriptionThis command instructs the plugin to turn its background thinking on or off. Qianhong disables background thinking when two plugins are playing each other or when the user manually disables it. (Qianhong will always tell the plugin to turn BG thinking on or off at the start of each game.) +

    +If the plugin does not support BG thinking (as specified by "BGTHINK 0" in the info mode, it does not need to support this command.
    + +

    TIMEOUT
    + + + + +
    CommandTIMEOUT
    Responses(none)
    DescriptionThis command is sent by Qianhong after the user-specified time limit is up and the AI or HINTS command has not completed. This command similar to the ABORT command. In fact, the only difference is that the AI must stop thinking and return a valid move or hints list instead of giving the ABORTED response. As with the ABORT command, the only response is from a pending AI or HINTS command. (The plugin must not send a response if the AI is idle.) +

    +The plugin may choose not to implement this command by specifying "TIMEOUT 0" in the info mode.
    + +

    That's it for the commands. If you have questions about them, or if my descriptions +are not very clear, there are a few things you can do to better understand the protocol: +

      +
    • Look at the source code for keplugin and VsccPlugin for examples. +
    • Run Qianhong with the "plugin_debug" parameter and watch the communication with +the plugins. +
    • Run a plugin directly (with the "-plugin" argument) and type in your commands. +(Both QHPlugin.exe and keplugin.exe have a special command, SCR, that displays the +game board to help you follow the game when you are running it by hand.) +
    + +

    The next section, 4. Tricky Stuff, describes +things you should watch out for when writing your plugin. + +


    +

    4. Tricky Stuff + +

    Testing Your Plugin
    +When you run Qianhong with the "plugin_debug" parameter, the "Choose AI Engine" dialog +shows a button you can use to run a set of tests on the plugins. Use this feature to +help ensure your plugin handles the commands correctly. + +

    Piped I/O
    +Be sure to flush STDOUT after every response! + +

    Response Time
    +If your AI takes more than a second for the AI and HINTS commands (most engines will), +then you must come up with some way to respond quickly to any incoming command. +The best way is probably to use a separate thread for your AI engine. You +might also use polling, but you'll need to use a non-blocking read for the input. + +

    Notation Conventions
    +Red is always on the "bottom" for ICCS moves and FEN strings. The following diagram +shows the ICCS notation coordinates and FEN row ordering (rows are described left-to-right): + +

    +              (Black)
    +     A  B  C  D  E  F  G  H  I
    +  9 [r][h][e][a][k][a][e][h][r] 9        (first FEN row)
    +     |  |  |  | \|/ |  |  |  |
    +  8  |--+--+--+--+--+--+--+--|  8        (2nd FEN row)
    +     |  |  |  | /|\ |  |  |  |
    +  7  |-[c]-+--+--+--+--+-[c]-|  7        (3rd FEN row)
    +     |  |  |  |  |  |  |  |  |
    +  6 [p]-+-[p]-+-[p]-+-[p]-+-[p] 6        (4th FEN row)
    +     |  |  |  |  |  |  |  |  |
    +  5  |-----------------------|  5        (5th FEN row)
    +     |                       |
    +  4  |-----------------------|  4        (6th FEN row)
    +     |  |  |  |  |  |  |  |  |
    +  3 (P)-+-(P)-+-(P)-+-(P)-+-(P) 3        (7th FEN row)
    +     |  |  |  |  |  |  |  |  |
    +  2  |-(C)-+--+--+--+--+-(C)-|  2        (8th FEN row)
    +     |  |  |  | \|/ |  |  |  |
    +  1  |--+--+--+--+--+--+--+--|  1        (9th FEN row)
    +     |  |  |  | /|\ |  |  |  |
    +  0 (R)(H)(E)(A)(K)(A)(E)(H)(R) 0        (last FEN row)
    +     A  B  C  D  E  F  G  H  I
    +               (Red)
    +
    + +

    Player Turns
    +Red goes first, unless a FEN command changes it to black's turn. When the plugin gets an +AI (or HINTS) command, it is to play (or think) for whichever side is to move next. +The AI is never explicitly told which side it is playing. + +

    BG Thinking
    +If you implement background thinking, you should start thinking after an AI command, and think ahead for the same color that the AI command moved for. If something unexpected comes along (UNDO, BAN, PLAY an unanticipated move, etc.), you'll have to bail on the thinking. Since a plugin is only used for one side of the game (except when a human is playing his/her self--which doesn't use AI commands), you won't get AI commands for both sides of the board. + +


    + + +

    Copyright © 2004 Jeremy Craner + + + diff --git a/QH2UCCI/MAKEFILE.BAT b/QH2UCCI/MAKEFILE.BAT new file mode 100644 index 0000000..5f23ecc --- /dev/null +++ b/QH2UCCI/MAKEFILE.BAT @@ -0,0 +1,5 @@ +@ECHO OFF +RC ..\RES\QH2UCCI.RC +CL /DNDEBUG /O2 /W3 /Fe..\BIN\QH2UCCI.EXE ..\BASE\PIPE.CPP ..\ELEEYE\UCCI.CPP QH2UCCI.CPP SHLWAPI.LIB ..\RES\QH2UCCI.RES +DEL ..\RES\QH2UCCI.RES +DEL *.OBJ diff --git a/QH2UCCI/QH2UCCI.INI b/QH2UCCI/QH2UCCI.INI new file mode 100644 index 0000000..b27d08a --- /dev/null +++ b/QH2UCCI/QH2UCCI.INI @@ -0,0 +1,7 @@ +[QH2UCCI] +Command=keplugin.exe -plugin +Name=ki11egg v0.2 (MRSD2) +Copyright=Copyright (C) Tzer-jen Wei +Author=Tzer-jen Wei +User=Computer Chinese Chess League +Level=15 diff --git a/QH2UCCI/makefile.sh b/QH2UCCI/makefile.sh new file mode 100644 index 0000000..6ea6094 --- /dev/null +++ b/QH2UCCI/makefile.sh @@ -0,0 +1 @@ +g++ -DNDEBUG -O4 -Wall -oQH2UCCI.EXE ../base/pipe.cpp ../eleeye/ucci.cpp qh2ucci.cpp \ No newline at end of file diff --git a/QH2UCCI/qh2ucci.cpp b/QH2UCCI/qh2ucci.cpp new file mode 100644 index 0000000..7cc4fba --- /dev/null +++ b/QH2UCCI/qh2ucci.cpp @@ -0,0 +1,304 @@ +/* +QH2UCCI - a Qianhong to UCCI Protocol Adapter +Designed by Morning Yellow, Version: 1.6, Last Modified: Jun. 2006 +Copyright (C) 2004-2006 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include "../base/base.h" +#include "../base/base2.h" +#include "../base/parse.h" +#include "../base/pipe.h" +#include "../eleeye/ucci.h" + +const int MAX_CHAR = 1024; // 配置文件的最大长度 + +// ICCS格式转换:浅红到UCCI +inline uint32_t ICCS_QH_UCCI(const char *szIccs) { + union { + char c[4]; + uint32_t dw; + } Ret; + Ret.c[0] = szIccs[0] - 'A' + 'a'; + Ret.c[1] = szIccs[1]; + Ret.c[2] = szIccs[3] - 'A' + 'a'; + Ret.c[3] = szIccs[4]; + return Ret.dw; +} + +// ICCS格式转换:UCCI到浅红 +inline void ICCS_UCCI_QH(char *szIccs, uint32_t dwMoveStr) { + char *lpMoveStr; + lpMoveStr = (char *) &dwMoveStr; + szIccs[0] = lpMoveStr[0] - 'a' + 'A'; + szIccs[1] = lpMoveStr[1]; + szIccs[2] = '-'; + szIccs[3] = lpMoveStr[2] - 'a' + 'A'; + szIccs[4] = lpMoveStr[3]; + szIccs[5] = '\0'; +} + +// 把UCCI引擎的FEN串转换为浅红插件的FEN串 +static void FenUcci2QH(char *lp, bool bBlackMoves) { + while (*lp != ' ' && *lp != '\0') { + switch (*lp) { + case 'B': + *lp = 'E'; + break; + case 'N': + *lp = 'H'; + break; + case 'b': + *lp = 'e'; + break; + case 'n': + *lp = 'h'; + break; + default: + break; + } + lp ++; + } + strcpy(lp, bBlackMoves ? " b - - - 1" : " w - - - 1"); +} + +PipeStruct pipePlugin; + +// 向浅红插件发送指令 +inline void Adapter2QH(const char *szLineStr) { + pipePlugin.LineOutput(szLineStr); + printf("info lineoutput [%s]\n", szLineStr); + fflush(stdout); +} + +// 接收浅红插件的反馈信息 +inline bool QH2Adapter(char *szLineStr) { + if (pipePlugin.LineInput(szLineStr)) { + printf("info lineinput [%s]\n", szLineStr); + fflush(stdout); + return true; + } else { + return false; + } +} + +inline void PrintLn(const char *sz) { + printf("%s\n", sz); + fflush(stdout); +} + +// 主函数 +int main(void) { + int i, nLevel, nThinkTime; + bool bQuit, bBlackMoves, bTimeOut; + uint32_t dwMoveStr; + FILE *fpIniFile; + char *lp; + int64_t llTime; + UcciCommStruct UcciComm; + char szIccs[8]; + char szIniFile[MAX_CHAR], szCommand[MAX_CHAR]; + char szLineStr[LINE_INPUT_MAX_CHAR]; + + if (BootLine() != UCCI_COMM_UCCI) { + return 0; + } + // 收到"ucci"指令后,完成以下几个任务: + + // 1. 读取适配器配置文件"QH2UCCI.INI" + LocatePath(szIniFile, "QH2UCCI.INI"); + nLevel = 0; + fpIniFile = fopen(szIniFile, "rt"); + if (fpIniFile == NULL) { + PrintLn("uccierror"); + PrintLn("bye"); + return 0; + } + while (!feof(fpIniFile)) { + fgets(szLineStr, MAX_CHAR, fpIniFile); + lp = strchr(szLineStr, '\n'); + if (lp != NULL) { + *lp = '\0'; + } + lp = szLineStr; + if (false) { + } else if (StrEqvSkip(lp, "Command=")) { + LocatePath(szCommand, lp); + } else if (StrEqvSkip(lp, "Name=")) { + printf("id name %s\n", lp); + fflush(stdout); + } else if (StrEqvSkip(lp, "Copyright=")) { + printf("id copyright %s\n", lp); + fflush(stdout); + } else if (StrEqvSkip(lp, "Author=")) { + printf("id author %s\n", lp); + fflush(stdout); + } else if (StrEqvSkip(lp, "User=")) { + printf("id user %s\n", lp); + fflush(stdout); + } else if (StrEqvSkip(lp, "Level=")) { + nLevel = Str2Digit(lp, 0, 99); + } + } + fclose(fpIniFile); + + // 2. 初使化适配器参数 + bQuit = bBlackMoves = false; + pipePlugin.Open(szCommand); + sprintf(szLineStr, "LEVEL %d", nLevel); + Adapter2QH(szLineStr); + while (!QH2Adapter(szLineStr)) { + Idle(); + } + Adapter2QH("BGTHINK OFF"); + while (!QH2Adapter(szLineStr)) { + Idle(); + } + PrintLn("option usemillisec type check default true"); + PrintLn("option dualtime type label"); + PrintLn("ucciok"); + + // 3. 接收UCCI指令 + while (!bQuit) { + switch (IdleLine(UcciComm, false)) { + case UCCI_COMM_ISREADY: + PrintLn("readyok"); + break; + case UCCI_COMM_STOP: + PrintLn("nobestmove"); + break; + case UCCI_COMM_POSITION: + bBlackMoves = (strstr(UcciComm.szFenStr, " b") != NULL); + FenUcci2QH(UcciComm.szFenStr, bBlackMoves); + sprintf(szLineStr, "FEN %s", UcciComm.szFenStr); + Adapter2QH(szLineStr); + while (!QH2Adapter(szLineStr)) { + Idle(); + } + if (UcciComm.nMoveNum > 0) { + sprintf(szLineStr, "LOAD %d", UcciComm.nMoveNum); + Adapter2QH(szLineStr); + for (i = 0; i < UcciComm.nMoveNum; i ++) { + ICCS_UCCI_QH(szIccs, UcciComm.lpdwMovesCoord[i]); + Adapter2QH(szIccs); + bBlackMoves = !bBlackMoves; + } + while (!QH2Adapter(szLineStr)) { + Idle(); + } + } + break; + case UCCI_COMM_BANMOVES: + if (UcciComm.nBanMoveNum > 0) { + sprintf(szLineStr, "BAN %d", UcciComm.nMoveNum); + Adapter2QH(szLineStr); + for (i = 0; i < UcciComm.nBanMoveNum; i ++) { + ICCS_UCCI_QH(szIccs, UcciComm.lpdwBanMovesCoord[i]); + Adapter2QH(szIccs); + } + while (!QH2Adapter(szLineStr)) { + Idle(); + } + } + break; + case UCCI_COMM_GO: + switch (UcciComm.Go) { + case UCCI_GO_DEPTH: + case UCCI_GO_NODES: + // 适配器不支持"depth"和"nodes"思考模式 + PrintLn("nobestmove"); + break; + case UCCI_GO_TIME_MOVESTOGO: + case UCCI_GO_TIME_INCREMENT: + // 在"time"思考模式下,分配适当的时间作思考 + if (UcciComm.Go == UCCI_GO_TIME_MOVESTOGO) { + nThinkTime = UcciComm.nTime / UcciComm.nMovesToGo; + } else { + nThinkTime = UcciComm.nTime / 20 + UcciComm.nIncrement; + } + Adapter2QH("AI"); + llTime = GetTime(); + bTimeOut = false; + while (!bQuit) { + // 等待思考结果,需要检查以下几方面内容: + // (1) 控制时间 + if (!bTimeOut && (int) (GetTime() - llTime) > nThinkTime) { + Adapter2QH("TIMEOUT"); + bTimeOut = true; + } + // (2) 处理UCCI指令 + switch (BusyLine(UcciComm, false)) { + case UCCI_COMM_STOP: + if (!bTimeOut) { + Adapter2QH("TIMEOUT"); + bTimeOut = true; + } + break; + case UCCI_COMM_QUIT: + Adapter2QH("ABORT"); + bQuit = true; + break; + default: + break; + } + // (3) 处理浅红插件的反馈信息 + if (QH2Adapter(szLineStr)) { + if (StrEqv(szLineStr, "ERROR") || StrEqv(szLineStr, "ABORTED")) { + PrintLn("nobestmove"); + } else { + dwMoveStr = ICCS_QH_UCCI(szLineStr); + printf("bestmove %.4s\n", (const char *) &dwMoveStr); + fflush(stdout); + } + Adapter2QH("UNDO"); + while (!QH2Adapter(szLineStr)) { + Idle(); + } + break; + } else { + Idle(); + } + } + break; + default: + break; + } + break; + case UCCI_COMM_QUIT: + bQuit = true; + break; + default: + break; + } + } + + // 4. 关闭浅红插件和管道 + Adapter2QH("QUIT"); + llTime = GetTime(); + while ((int) (GetTime() - llTime) < 1000) { + if (QH2Adapter(szLineStr)) { + if (StrEqv(szLineStr, "BYE")) { + break; + } + } + } + pipePlugin.Close(); + PrintLn("bye"); + return 0; +} diff --git a/README.TXT b/README.TXT new file mode 100644 index 0000000..b81c152 --- /dev/null +++ b/README.TXT @@ -0,0 +1,161 @@ +中国象棋对弈程序ElephantEye(象眼) 版本:3.13 + +黄晨 * 2008年3月 +( * 联系地址:上海计算机博弈研究所,eMail:webmaster@elephantbase.net) + +一、简介 + +  ElephantEye 是一款自由的中国象棋程序,在遵循《GNU宽松通用公共许可协议》(GNU Lesser General Public Licence)的前提下,广大象棋爱好者和程序设计师可以自由使用 ElephantEye 及其源程序。 +  ElephantEye 中文名称为“象眼”,它跟“马腿”和“炮架子”一起构成了中国象棋“棋盘上的第三维”。ElephantEye 通常与一个象棋棋谱编辑软件ElephantBoard 配合使用,寓意有板有眼(英文 Board 的意思是“板”)。(注:现在 ElephantBoard 已更名为“象棋巫师”。) + +二、引擎协议 + +  ElephantEye 支持UCCI 3.0,浅红象棋用户可通过UCCI引擎适配器(UCCI2QH)调用ElephantEye引擎。 +(1) 支持的UCCI命令有: +  ucci +  setoption ... +  position {fen | startpos} [moves ] +  banmoves +  go [ponder | draw] ... +  ponderhit [draw] | stop +  probe {fen | startpos} [moves ] +  quit +(2) 可以返回的UCCI信息有: +  id {name | version | copyright | author | user } +  option ... +  ucciok +  info ... +  {nobestmove | bestmove [ponder ] [draw | resign]} + pophash [bestmove ] [lowerbound depth ] [upperbound depth ] +  bye + +三、参数设置 + +  ElephantEye 作为UCCI引擎,有若干可以设置的参数(可以直接在《象棋巫师》中设置)。 +(1) 开局库: +  默认的开局库为 ElephantEye 程序(ELEEYE.EXE)当前目录下的 BOOK.DAT,含有10,000个对称局面的着法。 +(2) 思考时间: +  限定思考深度通常不是很好的选择,建议给定限时让程序自动分配时间。而在解杀局或分析局面时,则可让程序无限制思考,并可随时中止思考。 +(3) 置换表大小: +  尽管置换表大小对程序的运行速度影响不大,默认16MB的设置已经足够,但 ElephantEye 还是提供了设置置换表大小的功能。在内存允许的情况下,下慢棋时可以适当增加置换表的大小,但建议不要超过物理内存的一半。 +(3) 裁剪程度: +  为加快程序的运算速度,ElephantEye 默认使用空着裁剪,并且产生负面影响的可能性很小。只有最低级别会禁用空着裁剪。 +(4) 知识量: +  知识量和局面评价的准确性有关,在 ElephantEye 的知识量等级中,只有最低级别是不采用局面评价函数的(只考虑子力价值),在解排局等不需要依靠审局知识来分析的局面时,可以尝试用这种设置。 +(5) 随机性: +  ElephantEye 设有4级随机性。随机性越大,程序越有可能走出它认为不是最好的着法,但“不是最好的着法”并非一点好处也没有,尤其在没有启用开局库时,适当增大随机性,可以避免程序在相同的局面下走出一样的着法。 + +四、规则 + +  从2.0版开始,ElephantEye除了支持“单方面长将判负”的规则外,还支持“长打判负”,“打”包括“将”和“捉”。由于程序复杂性方面的限制,只有以下三种情况被识别成“捉”: +    A. 马捉车或有根的炮兵(卒); +    B. 车捉有根的马炮兵(卒); +    C. 炮捉车或有根的马兵(卒)。 +  尽管 ElephantEye 在复杂的情况可能无法正确识别长打,但由于支持UCCI命令 banmoves ... ,一旦用户认为引擎走了“长打”的禁着,可以用《象棋巫师》的“设置禁着”功能让引擎强制变着。 + +五、博弈算法 + +  ElephantEye 属于偏向蛮力的象棋程序,使用了严谨而有效的博弈算法: +(1) 使用位行和位列的着法生成器: +  位行(BitRanks)和位列(BitFiles)有利于滑动棋子(车和炮)的着法(尤其是吃子着法)生成,位行和位列可以用查表来代替在射线上做的循环运算。在ElephantEye 中,位行和位列的技术不仅用在着法生成器中,也用到了牵制的判断上。 +(2) 静态局面搜索: +  在做静态搜索时,ElephantEye 搜索了吃子或解将的着法,在搜索吃子着法时,ElephantEye 过滤掉不重要的吃子,例如吃不过河的兵、吃不处于防守中的士象等着法,都不在静态搜索的范围之内。 +(3) 循环着法和长将检测: +  ElephantEye 可以识别循环着法,出现循环着法时可以判断哪方为长将,并且会利用禁止长将的规则来谋求优势,但目前 ElephantEye 还无法识别长捉。 +(4) 置换表: +  ElephantEye 参考了中国象棋程序“纵马奔流”的设计思路,使用深度优先和始终覆盖的双层置换表,并采用低出(高出)边界修正的置换表更新策略。 +(5) 带检验的空着裁剪: +  ElephantEye 使用 R=2 的空着裁剪,在残局阶段使用带检验的空着裁剪。 +(6) 迭代加深/吃子着法/杀手着法/历史表启发: +  ElephantEye 的着法排序非常简单清晰,依次是迭代加深着法、好的吃子着法、杀手着法和按历史表排序的生成着法。 +(7) 将军/唯一应将延伸: +  在选择性延伸上,ElephantEye 采用了将军和唯一应将延伸。 +(8) Alpha-Beta主要变例搜索: +  ElephantEye 使用传统意义上的递归式Alpha-Beta主要变例搜索。 +(9) 开局库: +  ElephantEye 的开局库共包含了10,000个对称着法,是从1990年到2005年全国象棋个人赛、团体赛、五羊杯、联赛等8,000局顶尖比赛中提取的。 +(10) 后台思考和时间分配策略: +  ElephantEye 支持后台思考功能,同时提供了时段制和加时制两种时间分配策略,会自动合理分配时间。 + +六、开局库 + +  ElephantEye 的开局库可由“ElephantEye 开局库制作工具”制作。运行制作工具后,首先要选择PGN棋谱所在的文件夹,然后保存为开局库文件(通常是 BOOK.DAT)。通常,用来生成开局库的棋谱数量越多,生成的开局库文件就越大。 +  为了使制作的开局库对 ElephantEye 生效,只需要把生成的开局库文件替换掉 ElephantEye 目录下的 BOOK.DAT 即可,也可以在《象棋巫师》的“引擎设置”对话框中指定开局库文件。 + +七、局面评价函数库 + +  ElephantEye 从2.1版开始,程序的搜索部分和局面评价部分就分离了,搜索部分通过调用API函数的形式与局面评价部分耦合。 +  其他象棋程序设计师可以在 ElephantEye 的基础上更自由地发挥。根据LGPL协议,搜索和局面评价这两个部分都作为独立的程序库,运用其中任何一部分都只需要公开该部分的源程序即可。换句话说,如果局面评价部分没有使用任何开放代码,那么程序设计师就没有义务公开这部分的源程序,搜索部分也是如此。 +  ElephantEye 的局面评价API函数接口定义如下: +    A. 局面评价引擎名称:const char *GetEngineName(void); +    B. 局面预评价函数接口:void PreEvaluate(PositionStruct *lppos, PreEvalStruct *lpPreEval); +    C. 局面评价函数接口:int Evaluate(const PositionStruct *lppos, int vlAlpha, int vlBeta); +  其中 PositionStruct 和 PreEvalStruct 必须分别符合 position.h 和 pregen.h 中定义的结构。     + +八、源程序 + +  ElephantEye 的源程序包括9个模块,内容大致为: +(1) ucci.h/ucci.cpp +  UCCI命令解释模块,包括 Windows 和 Unix 下的行输入接收程序; +(2) pregen.h/pregen.cpp +  Zobrist 数组和着法预置表的生成模块。ElephantEye 的预置表分两个部分,一是滑动棋子的着法预置表(包括不吃子、车吃子、炮吃子和隔两子吃子),它是实现位行和位列技术的基础;二是其他棋子的着法预置表,使得着法生成时避免了烦琐的边界判断。 +(3) position.h/position.cpp +  主要描述着法和局面的数据结构及功能。局面的处理是本模块的重点,处理内容包括局面初始化、FEN串导入、棋子移动、杀手着法的合理性判断、将军判断、长将和循环检测、子力价值分调整等过程,还包括5个子力位置价值表。 +(4) genmoves.cpp +  着法生成器,包括生成吃子着法和生成不吃子着法的两个,但不能只生成解除将军的着法。在生成吃子着法的同时赋予每个着法以相应的MVV(LVA)(或称准SEE)值。该模块还有一个专门判断棋子是否有保护的函数,来计算MVV(LVA)值,对于有保护的棋子,计算MVV-LVA的值(小于零不计),对于无保护的棋子,只计算MVV的值。因此,判断棋子是否有根的程序也包括在本模块中。 +(5) hash.h/hash.cpp +  置换表、历史表和着法列表管理模块,包括置换表的分配和存取、主要变例获取等操作。 +(6) book.h/book.cpp +  开局库读取模块。 +(7) movesort.h/movesort.cpp +  着法列表排序模块。 +(8) search.h/search.cpp +  搜索模块,除了静态搜索、完全搜索和根结点搜索这三个主要过程外,还包括迭代加深控制、后台思考、时间分配、搜索参数统计和搜索信息输出等内容。该模块是整个程序的核心模块。 +(9) eleeye.cpp +  主程序(即 main 函数)。 +(10) preeval.h/preeval.cpp +  子力位置数组预生成器,ElephantEye 根据“进攻/防守”和“开局/中局/残局”两个参数线性调整子力位置数组。 +(11) evaluate.cpp +  局面评价函数,ElephantEye 采用了四级偷懒评价的机制,最粗的层次只评价特殊棋型,进一层次评价牵制,再进一层次评价车的灵活性,最高层次还评价马的阻碍。 + +九、程序表现 + +  ElephantEye 的设计重点在搜索算法,但在知识上比较欠缺。在2.8GHz的处理器上每秒可搜索约1,000,000个结点(包括常规搜索和静态搜索),一般的中局局面在1分钟内可搜索约11层。 +  在棋力上,ElephantEye 和“棋隐”、SaoLa (象棋挑战者)等程序具有同等水平,但由于局面评估函数上的缺陷,ElephantEye 距离顶尖的商业象棋软件(谢谢大师、象棋世家、象棋奇兵、棋天大圣等)尚有一定的差距。 +  ElephantEye 在联众、弈天等象棋对弈网站上作过测试,用等级分来衡量,联众网的战绩在2500分左右,弈天网快棋的战绩在2000分左右,慢棋在1500分左右。 +  2005年9月在台湾象棋软件爱好者施金山先生的帮助下,ElephantEye 参加了在台北举行的第10届ICGA电脑奥林匹克大赛中国象棋组比赛,战绩是7胜5和14负,在14个程序中排名第11;2006年8月 ElephantEye 参加了在北京举行的全国首届计算机博弈锦标赛,战绩是7胜2和11负,在18个程序中排名第7。 + +十、相关资源 + +  ElephantEye的源程序发布在SourceForge的XiangQi Wizard项目中,其页面是: +    http://sourceforge.net/projects/xqwizard/ +  ElephantEye的版本改进,实时同步地发布在SourceForge的SVN站点上,获取地址是: +    https://xqwizard.svn.sourceforge.net/svnroot/xqwizard/ +  您可以使用 TortoiseSVN 等SVN客户端程序获取到最新的(跟开发者完全同步的)代码,TortoiseSVN 的使用介绍和下载地址是: +    http://sourceforge.net/projects/tortoisesvn/ + +  ElephantEye 必须在支持UCCI(如《象棋巫师》)的象棋程序下运行,《象棋巫师》安装程序包含了最近一个版本的 ElephantEye。 +  《象棋巫师》简体中文版可以到下列站点下载: +    http://www.skycn.net/soft/24665.html (天空软件站) +    http://www.onlinedown.net/soft/38287.htm (华军软件园) +  《象棋巫师》繁体中文版可以到下列站点下载: +    http://www.skycn.net/soft/35392.html (天空软件站) +    http://www.onlinedown.net/soft/47666.htm (华军软件园) + +  ElephantEye 的源程序包除了 ElephantEye 本身的源程序外,还包括以下几个附加模块: +  (1) 通用低级接口(utility):提供了汇编指令、位棋盘、数值计算、Visual Basic 下的原子语句等功能; +  (2) 中国象棋规则模块(cchess):为其他软件使用 ElephantEye 代码提供了接口; +  (3) 开局库制作模块(BOOK):制作开局库BOOK.DAT的代码; +  (4) UCCI引擎联赛模拟器(LEAGUE):为UCCI引擎测试和比赛提供了自动批量对局的平台; +  (5) UCCI引擎搜索树分析器(TREE):UCCI引擎(支持UCCI 2.2+)的搜索路线分析工具; +  (6) XQF棋谱工具(XQFTOOLS):提供象棋演播室棋谱(*.XQF)、弈天棋谱(*.MXQ)和QQ象棋棋谱(*.CHE)转换为可移植棋谱(*.PGN)的工具; +  (7) 浅红象棋适配器(UCCI2QH):为浅红象棋调用UCCI引擎提供了接口; +  (8) 浅红象棋引擎支持UCCI的适配器(QH2UCCI):为“梦入神蛋”浅红象棋加入UCCI引擎测试提供了接口; +  (9) BBS Chess(BBSCHESS):一个用 Visual Basic 制作的国际象棋局面设置工具,可在各高校BBS上粘贴彩色的国际象棋局面; +  (10) 棋盘图片生成器(FEN2BMP):一个可以把国际象棋和中国象棋的FEN文件转换成BMP文件的实用工具; +  (11) 其他工具(MISC):包括BASE64转码、BIG5/GB转码、UNIX文本转码、简易网络通讯、管道测试等工具。 +  (12) 说明文档(DOC):即《中国象棋程序设计探索》系列连载; +  (13) 参赛棋谱(CCGC):ElephantEye 参加首届全国计算机博弈锦标赛(CCGC)的全部棋谱。 + +  如果要获得关于 ElephantEye 的更加详细的信息,可登录象棋百科全书网: +    http://www.elephantbase.net/ \ No newline at end of file diff --git a/RES/CCHESS.ICO b/RES/CCHESS.ICO new file mode 100644 index 0000000..1f10612 Binary files /dev/null and b/RES/CCHESS.ICO differ diff --git a/RES/CCHESS.RC b/RES/CCHESS.RC new file mode 100644 index 0000000..93099f5 --- /dev/null +++ b/RES/CCHESS.RC @@ -0,0 +1,32 @@ +#include +1 ICON DISCARDABLE "CCHESS.ICO" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3, 1, 0, 0 + PRODUCTVERSION 3, 1, 0, 0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "Comments", "Chinese Chess Driver 3.1, (C) 2004-2007 www.elephantbase.net\0" + VALUE "CompanyName", "Author: Morning Yellow (* Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)\0" + VALUE "FileDescription", "Chinese Chess Driver 3.1\0" + VALUE "FileVersion", "3.1\0" + VALUE "InternalName", "Chinese Chess Driver 3.1\0" + VALUE "LegalCopyright", "Chinese Chess Driver 3.1, (C) 2004-2007 www.elephantbase.net\0" + VALUE "LegalTrademarks", "www.elephantbase.net\0" + VALUE "OriginalFilename", "CCHESS.DLL\0" + VALUE "ProductName", "Chinese Chess Driver 3.1\0" + VALUE "ProductVersion", "3.1\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/RES/CCM2PGN.ICO b/RES/CCM2PGN.ICO new file mode 100644 index 0000000..abf8c4d Binary files /dev/null and b/RES/CCM2PGN.ICO differ diff --git a/RES/CCM2PGN.RC b/RES/CCM2PGN.RC new file mode 100644 index 0000000..09efe0b --- /dev/null +++ b/RES/CCM2PGN.RC @@ -0,0 +1,32 @@ +#include +1 ICON DISCARDABLE "CCM2PGN.ICO" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3, 1, 4, 0 + PRODUCTVERSION 3, 1, 4, 0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "Comments", "CCM2PGN 3.14, (C) 2004-2008 www.elephantbase.net\0" + VALUE "CompanyName", "Author: Morning Yellow (* Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)\0" + VALUE "FileDescription", "CCM2PGN 3.14\0" + VALUE "FileVersion", "3.14\0" + VALUE "InternalName", "CCM2PGN 3.14\0" + VALUE "LegalCopyright", "CCM2PGN 3.14, (C) 2004-2008 www.elephantbase.net\0" + VALUE "LegalTrademarks", "www.elephantbase.net\0" + VALUE "OriginalFilename", "CCM2PGN.EXE\0" + VALUE "ProductName", "CCM2PGN 3.14\0" + VALUE "ProductVersion", "3.14\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/RES/CHE2PGN.ICO b/RES/CHE2PGN.ICO new file mode 100644 index 0000000..32885aa Binary files /dev/null and b/RES/CHE2PGN.ICO differ diff --git a/RES/CHE2PGN.RC b/RES/CHE2PGN.RC new file mode 100644 index 0000000..dcef362 --- /dev/null +++ b/RES/CHE2PGN.RC @@ -0,0 +1,32 @@ +#include +1 ICON DISCARDABLE "CHE2PGN.ICO" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3, 1, 4, 0 + PRODUCTVERSION 3, 1, 4, 0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "Comments", "CHE2PGN 3.14, (C) 2004-2008 www.elephantbase.net\0" + VALUE "CompanyName", "Author: Morning Yellow (* Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)\0" + VALUE "FileDescription", "CHE2PGN 3.14\0" + VALUE "FileVersion", "3.14\0" + VALUE "InternalName", "CHE2PGN 3.14\0" + VALUE "LegalCopyright", "CHE2PGN 3.14, (C) 2004-2008 www.elephantbase.net\0" + VALUE "LegalTrademarks", "www.elephantbase.net\0" + VALUE "OriginalFilename", "CHE2PGN.EXE\0" + VALUE "ProductName", "CHE2PGN 3.14\0" + VALUE "ProductVersion", "3.14\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/RES/CHN2PGN.ICO b/RES/CHN2PGN.ICO new file mode 100644 index 0000000..6efb315 Binary files /dev/null and b/RES/CHN2PGN.ICO differ diff --git a/RES/CHN2PGN.RC b/RES/CHN2PGN.RC new file mode 100644 index 0000000..0cc7b5e --- /dev/null +++ b/RES/CHN2PGN.RC @@ -0,0 +1,32 @@ +#include +1 ICON DISCARDABLE "CHN2PGN.ICO" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3, 1, 4, 0 + PRODUCTVERSION 3, 1, 4, 0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "Comments", "CHN2PGN 3.14, (C) 2004-2008 www.elephantbase.net\0" + VALUE "CompanyName", "Author: Morning Yellow (* Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)\0" + VALUE "FileDescription", "CHN2PGN 3.14\0" + VALUE "FileVersion", "3.14\0" + VALUE "InternalName", "CHN2PGN 3.14\0" + VALUE "LegalCopyright", "CHN2PGN 3.14, (C) 2004-2008 www.elephantbase.net\0" + VALUE "LegalTrademarks", "www.elephantbase.net\0" + VALUE "OriginalFilename", "CHN2PGN.EXE\0" + VALUE "ProductName", "CHN2PGN 3.14\0" + VALUE "ProductVersion", "3.14\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/RES/ELEEYE.ICO b/RES/ELEEYE.ICO new file mode 100644 index 0000000..f5c5671 Binary files /dev/null and b/RES/ELEEYE.ICO differ diff --git a/RES/ELEEYE.RC b/RES/ELEEYE.RC new file mode 100644 index 0000000..daa5dcd --- /dev/null +++ b/RES/ELEEYE.RC @@ -0,0 +1,32 @@ +#include +1 ICON DISCARDABLE "ELEEYE.ICO" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3, 1, 0, 0 + PRODUCTVERSION 3, 1, 0, 0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "Comments", "ElephantEye 3.1, (C) 2004-2007 www.elephantbase.net\0" + VALUE "CompanyName", "Author: Morning Yellow (* Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)\0" + VALUE "FileDescription", "ElephantEye\0" + VALUE "FileVersion", "3.1\0" + VALUE "InternalName", "ElephantEye\0" + VALUE "LegalCopyright", "ElephantEye 3.1, (C) 2004-2007 www.elephantbase.net\0" + VALUE "LegalTrademarks", "www.elephantbase.net\0" + VALUE "OriginalFilename", "ELEEYE.EXE\0" + VALUE "ProductName", "ElephantEye\0" + VALUE "ProductVersion", "3.1\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/RES/EVALUATE.RC b/RES/EVALUATE.RC new file mode 100644 index 0000000..1b7466d --- /dev/null +++ b/RES/EVALUATE.RC @@ -0,0 +1,31 @@ +#include +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3, 0, 1, 0 + PRODUCTVERSION 3, 0, 1, 0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "Comments", "ElephantEye Evaluation Module 3.01, (C) 2004-2007 www.elephantbase.net\0" + VALUE "CompanyName", "Author: Morning Yellow (* Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)\0" + VALUE "FileDescription", "ElephantEye Evaluation Module 3.01\0" + VALUE "FileVersion", "3.01\0" + VALUE "InternalName", "ElephantEye Evaluation Module 3.01\0" + VALUE "LegalCopyright", "ElephantEye Evaluation Module 3.01, (C) 2004-2007 www.elephantbase.net\0" + VALUE "LegalTrademarks", "www.elephantbase.net\0" + VALUE "OriginalFilename", "EVALUATE.DLL\0" + VALUE "ProductName", "ElephantEye Evaluation Module 3.01\0" + VALUE "ProductVersion", "3.01\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/RES/MAKEBOOK.ICO b/RES/MAKEBOOK.ICO new file mode 100644 index 0000000..0d1ef17 Binary files /dev/null and b/RES/MAKEBOOK.ICO differ diff --git a/RES/MAKEBOOK.RC b/RES/MAKEBOOK.RC new file mode 100644 index 0000000..8a812f3 --- /dev/null +++ b/RES/MAKEBOOK.RC @@ -0,0 +1,31 @@ +#include +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3, 1, 0, 0 + PRODUCTVERSION 3, 1, 0, 0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "Comments", "ElephantEye Book Maker 3.1, (C) 2004-2007 www.elephantbase.net\0" + VALUE "CompanyName", "Author: Morning Yellow (* Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)\0" + VALUE "FileDescription", "ElephantEye Book Maker\0" + VALUE "FileVersion", "3.1\0" + VALUE "InternalName", "ElephantEye Book Maker\0" + VALUE "LegalCopyright", "ElephantEye Book Maker 3.1, (C) 2004-2007 www.elephantbase.net\0" + VALUE "LegalTrademarks", "www.elephantbase.net\0" + VALUE "OriginalFilename", "MAKEBOOK.DLL\0" + VALUE "ProductName", "ElephantEye Book Maker\0" + VALUE "ProductVersion", "3.1\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/RES/MXQ2PGN.ICO b/RES/MXQ2PGN.ICO new file mode 100644 index 0000000..1e38500 Binary files /dev/null and b/RES/MXQ2PGN.ICO differ diff --git a/RES/MXQ2PGN.RC b/RES/MXQ2PGN.RC new file mode 100644 index 0000000..db6e0a8 --- /dev/null +++ b/RES/MXQ2PGN.RC @@ -0,0 +1,32 @@ +#include +1 ICON DISCARDABLE "MXQ2PGN.ICO" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3, 1, 4, 0 + PRODUCTVERSION 3, 1, 4, 0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "Comments", "MXQ2PGN 3.14, (C) 2004-2008 www.elephantbase.net\0" + VALUE "CompanyName", "Author: Morning Yellow (* Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)\0" + VALUE "FileDescription", "MXQ2PGN 3.14\0" + VALUE "FileVersion", "3.14\0" + VALUE "InternalName", "MXQ2PGN 3.14\0" + VALUE "LegalCopyright", "MXQ2PGN 3.14, (C) 2004-2008 www.elephantbase.net\0" + VALUE "LegalTrademarks", "www.elephantbase.net\0" + VALUE "OriginalFilename", "MXQ2PGN.EXE\0" + VALUE "ProductName", "MXQ2PGN 3.14\0" + VALUE "ProductVersion", "3.14\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/RES/MXQFCONV.ICO b/RES/MXQFCONV.ICO new file mode 100644 index 0000000..7b9a830 Binary files /dev/null and b/RES/MXQFCONV.ICO differ diff --git a/RES/MXQFCONV.RC b/RES/MXQFCONV.RC new file mode 100644 index 0000000..f245181 --- /dev/null +++ b/RES/MXQFCONV.RC @@ -0,0 +1,32 @@ +#include +1 ICON DISCARDABLE "MXQFCONV.ICO" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3, 1, 4, 0 + PRODUCTVERSION 3, 1, 4, 0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "Comments", "MXQ/XQF/CHE/CHN/CCM->PGN Convertor 3.14, (C) 2004-2008 www.elephantbase.net\0" + VALUE "CompanyName", "Author: Morning Yellow (* Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)\0" + VALUE "FileDescription", "MXQ/XQF/CHE/CHN/CCM->PGN Convertor\0" + VALUE "FileVersion", "3.14\0" + VALUE "InternalName", "MXQ/XQF/CHE/CHN/CCM->PGN Convertor\0" + VALUE "LegalCopyright", "MXQ/XQF/CHE/CHN/CCM->PGN Convertor 3.14, (C) 2004-2008 www.elephantbase.net\0" + VALUE "LegalTrademarks", "www.elephantbase.net\0" + VALUE "OriginalFilename", "MXQFCONV.EXE\0" + VALUE "ProductName", "MXQ/XQF/CHE/CHN/CCM->PGN Convertor\0" + VALUE "ProductVersion", "3.14\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/RES/PGN2XQF.ICO b/RES/PGN2XQF.ICO new file mode 100644 index 0000000..cbc192b Binary files /dev/null and b/RES/PGN2XQF.ICO differ diff --git a/RES/PGN2XQF.RC b/RES/PGN2XQF.RC new file mode 100644 index 0000000..85965a7 --- /dev/null +++ b/RES/PGN2XQF.RC @@ -0,0 +1,32 @@ +#include +1 ICON DISCARDABLE "PGN2XQF.ICO" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 2, 0, 2, 0 + PRODUCTVERSION 2, 0, 2, 0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "Comments", "PGN2XQF 2.02, (C) 2004-2007 www.elephantbase.net\0" + VALUE "CompanyName", "Author: Morning Yellow (* Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)\0" + VALUE "FileDescription", "PGN2XQF 2.02\0" + VALUE "FileVersion", "2.02\0" + VALUE "InternalName", "PGN2XQF 2.02\0" + VALUE "LegalCopyright", "PGN2XQF 2.02, (C) 2004-2007 www.elephantbase.net\0" + VALUE "LegalTrademarks", "www.elephantbase.net\0" + VALUE "OriginalFilename", "PGN2XQF.EXE\0" + VALUE "ProductName", "PGN2XQF 2.02\0" + VALUE "ProductVersion", "2.02\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/RES/QH2UCCI.RC b/RES/QH2UCCI.RC new file mode 100644 index 0000000..d4abe82 --- /dev/null +++ b/RES/QH2UCCI.RC @@ -0,0 +1,32 @@ +#include +1 ICON DISCARDABLE "UCCI2QH.ICO" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1, 6, 0, 0 + PRODUCTVERSION 1, 6, 0, 0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "Comments", "QH2UCCI 1.6, (C) 2004-2006 www.elephantbase.net\0" + VALUE "CompanyName", "Author: Morning Yellow (* Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)\0" + VALUE "FileDescription", "QH2UCCI 1.6\0" + VALUE "FileVersion", "1.6\0" + VALUE "InternalName", "QH2UCCI 1.6\0" + VALUE "LegalCopyright", "QH2UCCI 1.6, (C) 2004-2006 www.elephantbase.net\0" + VALUE "LegalTrademarks", "www.elephantbase.net\0" + VALUE "OriginalFilename", "QH2UCCI.EXE\0" + VALUE "ProductName", "QH2UCCI 1.6\0" + VALUE "ProductVersion", "1.6\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/RES/QIANHONG.ICO b/RES/QIANHONG.ICO new file mode 100644 index 0000000..ae7016a Binary files /dev/null and b/RES/QIANHONG.ICO differ diff --git a/RES/UCCI2QH.ICO b/RES/UCCI2QH.ICO new file mode 100644 index 0000000..0677693 Binary files /dev/null and b/RES/UCCI2QH.ICO differ diff --git a/RES/UCCI2QH.RC b/RES/UCCI2QH.RC new file mode 100644 index 0000000..bd9eea9 --- /dev/null +++ b/RES/UCCI2QH.RC @@ -0,0 +1,32 @@ +#include +1 ICON DISCARDABLE "UCCI2QH.ICO" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 2, 0, 0, 0 + PRODUCTVERSION 2, 0, 0, 0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "Comments", "UCCI2QH 2.0, (C) 2004-2007 www.elephantbase.net\0" + VALUE "CompanyName", "Author: Morning Yellow (* Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)\0" + VALUE "FileDescription", "UCCI2QH 2.0\0" + VALUE "FileVersion", "2.0\0" + VALUE "InternalName", "UCCI2QH 2.0\0" + VALUE "LegalCopyright", "UCCI2QH 2.0, (C) 2004-2007 www.elephantbase.net\0" + VALUE "LegalTrademarks", "www.elephantbase.net\0" + VALUE "OriginalFilename", "UCCI2QH.EXE\0" + VALUE "ProductName", "UCCI2QH 2.0\0" + VALUE "ProductVersion", "2.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/RES/UCCILEAG.ICO b/RES/UCCILEAG.ICO new file mode 100644 index 0000000..548725c Binary files /dev/null and b/RES/UCCILEAG.ICO differ diff --git a/RES/UCCILEAG.RC b/RES/UCCILEAG.RC new file mode 100644 index 0000000..23cfcc1 --- /dev/null +++ b/RES/UCCILEAG.RC @@ -0,0 +1,32 @@ +#include +1 ICON DISCARDABLE "UCCILEAG.ICO" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3, 5, 0, 0 + PRODUCTVERSION 3, 5, 0, 0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "Comments", "UCCI-League Emulator 3.5, (C) 2004-2007 www.elephantbase.net\0" + VALUE "CompanyName", "Author: Morning Yellow (* Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)\0" + VALUE "FileDescription", "UCCI-League Emulator 3.5\0" + VALUE "FileVersion", "3.5\0" + VALUE "InternalName", "UCCI-League Emulator 3.5\0" + VALUE "LegalCopyright", "UCCI-League Emulator 3.5, (C) 2004-2007 www.elephantbase.net\0" + VALUE "LegalTrademarks", "www.elephantbase.net\0" + VALUE "OriginalFilename", "UCCILEAG.EXE\0" + VALUE "ProductName", "UCCI-League Emulator 3.5\0" + VALUE "ProductVersion", "3.5\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/RES/WIZARD.BMP b/RES/WIZARD.BMP new file mode 100644 index 0000000..cd6d2ff Binary files /dev/null and b/RES/WIZARD.BMP differ diff --git a/RES/WIZARD.ICO b/RES/WIZARD.ICO new file mode 100644 index 0000000..701aeca Binary files /dev/null and b/RES/WIZARD.ICO differ diff --git a/RES/XQF2PGN.ICO b/RES/XQF2PGN.ICO new file mode 100644 index 0000000..cfa8dc0 Binary files /dev/null and b/RES/XQF2PGN.ICO differ diff --git a/RES/XQF2PGN.RC b/RES/XQF2PGN.RC new file mode 100644 index 0000000..03d7561 --- /dev/null +++ b/RES/XQF2PGN.RC @@ -0,0 +1,32 @@ +#include +1 ICON DISCARDABLE "XQF2PGN.ICO" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 2, 0, 2, 0 + PRODUCTVERSION 2, 0, 2, 0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "Comments", "XQF2PGN 2.02, (C) 2004-2007 www.elephantbase.net\0" + VALUE "CompanyName", "Author: Morning Yellow (* Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)\0" + VALUE "FileDescription", "XQF2PGN 2.02\0" + VALUE "FileVersion", "2.02\0" + VALUE "InternalName", "XQF2PGN 2.02\0" + VALUE "LegalCopyright", "XQF2PGN 2.02, (C) 2004-2007 www.elephantbase.net\0" + VALUE "LegalTrademarks", "www.elephantbase.net\0" + VALUE "OriginalFilename", "XQF2PGN.EXE\0" + VALUE "ProductName", "XQF2PGN 2.02\0" + VALUE "ProductVersion", "2.02\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/RES/XQWSETUP.ICO b/RES/XQWSETUP.ICO new file mode 100644 index 0000000..4fd0ae0 Binary files /dev/null and b/RES/XQWSETUP.ICO differ diff --git a/SYSTEM/BIG2GB.OCX b/SYSTEM/BIG2GB.OCX new file mode 100644 index 0000000..cb8f8a0 Binary files /dev/null and b/SYSTEM/BIG2GB.OCX differ diff --git a/SYSTEM/MSCOMCTL.OCX b/SYSTEM/MSCOMCTL.OCX new file mode 100644 index 0000000..eb2505c Binary files /dev/null and b/SYSTEM/MSCOMCTL.OCX differ diff --git a/SYSTEM/MSINET.OCX b/SYSTEM/MSINET.OCX new file mode 100644 index 0000000..291ffd4 Binary files /dev/null and b/SYSTEM/MSINET.OCX differ diff --git a/TEST/BATCH.TXT b/TEST/BATCH.TXT new file mode 100644 index 0000000..527f3d5 --- /dev/null +++ b/TEST/BATCH.TXT @@ -0,0 +1,483 @@ +ucci +setoption batch on +position fen 4C4/4a4/b2ank2b/9/9/1RNR1crC1/3r1p3/3cKA3/4A4/4n4 w - - 0 1 +go depth 10 +position fen 9/2Cca4/3k1C3/4P1p2/4N1b2/4R1r2/4c1n2/3p1n3/2rNK4/9 w - - 0 1 +go depth 10 +position fen 9/4a4/3k1a3/2R3r2/1N5n1/C7c/1N5n1/2R3r2/3p1p3/4K4 w - - 0 1 +go depth 10 +position fen 9/4P4/2NakaR2/3P1P3/2pP1cb2/3r1c3/1rPNppCn1/3K1A3/2p3n2/9 w - - 0 1 +go depth 10 +position fen 9/9/4Nk3/3c2p2/3r2P2/3p2B2/3p2r2/4KC3/9/9 w - - 0 1 +go depth 10 +position fen 9/9/3k1N3/9/1C5N1/9/1n5r1/9/3p1K3/9 w - - 0 1 +go depth 10 +position fen 9/9/3a1k3/9/1N5N1/4R4/1n5r1/9/3K1p3/9 w - - 0 1 +go depth 10 +position fen 9/9/5k1N1/4p1P1p/3P1C1C1/2N1r1r2/9/3ABK3/2ncpp3/1pBAc4 w - - 0 1 +go depth 10 +position fen 1nb1ka3/4a4/4c4/2p1C4/9/3Rcr3/P8/n3C4/4Apr2/4KA3 w - - 0 1 +go depth 10 +position fen 9/3Rak3/3a1n3/1PpP1PPR1/1P5n1/1rBp1pcp1/3C1p3/3Kcr3/9/9 w - - 0 1 +go depth 10 +position fen 1PP1kab2/1R2a4/4b3R/4C4/1C7/r8/9/2n6/3p1r3/4K4 w - - 0 1 +go depth 10 +position fen 3k5/5P3/3a1r3/9/9/9/9/2R6/7p1/4K4 w - - 0 1 +go depth 10 +position fen 3aka3/3P5/7R1/4r2C1/6C2/6R2/9/3p1n3/4p4/3K5 w - - 0 1 +go depth 10 +position fen 4ka3/2R1a4/7N1/9/9/9/4p4/2C6/2p1p1r2/1R3K3 w - - 0 1 +go depth 10 +position fen 4k1b2/4CP3/4b4/4p4/4P4/9/4n4/3KB4/4r4/4n1rC1 w - - 0 1 +go depth 10 +position fen 9/1P2k4/3a1a3/4P4/8r/9/2R6/3n5/4p4/5K3 w - - 0 1 +go depth 10 +position fen 3a1k3/1C7/3a1P3/4N4/9/3n2C2/9/9/1rp1p4/3K5 w - - 0 1 +go depth 10 +position fen 2bakcb2/1n1C1R3/9/4C4/2p1p1p2/9/2N6/6n2/3pAp1r1/4K3c w - - 0 1 +go depth 10 +position fen 4kar2/4a2nn/4bc3/RN1r5/2bC5/9/4p4/9/4p4/3p1K3 w - - 0 1 +go depth 10 +position fen 2bak1P2/4a4/9/6N2/9/9/9/C1nC5/1c1pRp2r/3cK4 w - - 0 1 +go depth 10 +position fen 9/3R5/2C1k4/9/1N2P4/9/9/5n3/1r2p4/3K5 w - - 0 1 +go depth 10 +position fen 2ca1k3/4P1r1R/4ba3/7Cp/8r/7C1/7n1/9/3p1pp2/3nK4 w - - 0 1 +go depth 10 +position fen 9/5k3/3R5/9/2N6/9/6N2/9/1pp1p1c2/CrBK5 w - - 0 1 +go depth 10 +position fen 1r4r2/3ca4/4k4/2pc1P3/9/9/9/9/5K1n1/5R1RC w - - 0 1 +go depth 10 +position fen 3akab2/1C6c/N3b4/9/1N7/9/9/C8/n4p3/rc2K1p2 w - - 0 1 +go depth 10 +position fen 2n6/6N2/3k5/2P6/9/9/2p6/1C6C/4p1r2/5K3 w - - 0 1 +go depth 10 +position fen 4kab2/1N1Pa4/4b4/3N2p2/6Pn1/9/2P6/2n6/2p1Ap3/3AK2p1 w - - 0 1 +go depth 10 +position fen 4k4/6P2/3rP2P1/2P6/9/9/9/9/9/4K4 w - - 0 1 +go depth 10 +position fen 2ba1kbRC/2N1a4/9/4p4/4c1p2/9/9/1p2B1r2/4r1n2/3K2B2 w - - 0 1 +go depth 10 +position fen 2bak1b1N/9/2n1ca3/3R1C3/9/9/9/C3B4/c3p2p1/1rB2K3 w - - 0 1 +go depth 10 +position fen 2b2a2c/4a1P2/3kb4/2PN5/2nR5/9/4n4/9/4p4/5K1p1 w - - 0 1 +go depth 10 +position fen 3k2r2/2P1a4/9/9/4N4/7r1/9/4B3C/9/4RK3 w - - 0 1 +go depth 10 +position fen C3kab1r/2C1a4/b1n5c/1N7/9/9/9/9/2pc1p3/4K4 w - - 0 1 +go depth 10 +position fen 3nk4/2P1a3R/4r4/4P4/2NC5/9/9/9/4p1p2/2r1cK3 w - - 0 1 +go depth 10 +position fen 5k2c/3PP3r/5n2b/6N2/9/8C/9/9/2r2p3/4K4 w - - 0 1 +go depth 10 +position fen 5k3/1P7/2PP1a1P1/9/9/4R4/9/5p3/3p1p3/1p2K4 w - - 0 1 +go depth 10 +position fen 5k3/3Cc1P1r/2c2N3/9/9/9/9/9/3p2r2/3CKR3 w - - 0 1 +go depth 10 +position fen 3k1ar2/2P1a4/3P5/9/9/9/9/7C1/2p2p3/4K1C2 w - - 0 1 +go depth 10 +position fen r1b1ka3/3Pa4/b8/9/9/9/9/1C4p2/9/c3K4 w - - 0 1 +go depth 10 +position fen 4k1P2/4a1P2/3Rb4/6R2/9/N8/9/4p4/2p1pp3/3K2p2 w - - 0 1 +go depth 10 +position fen 9/3Pak3/9/4P4/2b6/9/9/9/9/4K4 w - - 0 1 +go depth 10 +position fen 3k5/4c4/9/9/RR1N5/9/2n6/3p5/4p2r1/3K5 w - - 0 1 +go depth 10 +position fen 6b2/4ak1C1/2N2aR2/4P4/2b6/9/6p1P/4B1p1r/r3Ap3/3AK3c w - - 0 1 +go depth 10 +position fen 3P5/4ak3/3a2R1b/9/5P2N/9/9/9/2rr5/4K4 w - - 0 1 +go depth 10 +position fen 2ba1k3/3Ra4/2N1b4/2R6/2C6/5r3/9/4rA3/5K3/9 w - - 0 1 +go depth 10 +position fen 4kC3/9/5P3/3R5/9/9/9/2p6/4r4/3K5 w - - 0 1 +go depth 10 +position fen 4k4/3Pa1P2/5P3/9/6b2/9/6n2/9/2C1p4/5K3 w - - 0 1 +go depth 10 +position fen 4ka3/4a4/8b/9/4N4/9/9/1R5C1/2pCp4/3K1nnc1 w - - 0 1 +go depth 10 +position fen 2bak1C2/3caP3/4b4/3N1Cn2/9/9/8n/9/4p4/5K3 w - - 0 1 +go depth 10 +position fen 2bak1P1r/4aP3/b5N2/9/9/9/9/9/5r3/2R1K4 w - - 0 1 +go depth 10 +position fen 3ak4/2PPa4/b3b4/2p1C1N2/3c5/1rB6/9/3p5/4p2p1/1CB2K3 w - - 0 1 +go depth 10 +position fen 3a1a3/3k2P2/9/9/9/3Nr4/9/7C1/4A2c1/1crRKA3 w - - 0 1 +go depth 10 +position fen 3ak1b2/4aPP2/8b/9/9/9/9/2p5r/1n1cA2CC/4K1cn1 w - - 0 1 +go depth 10 +position fen 6b2/2Nkn2P1/2Pab2r1/2R6/2Cn3c1/9/9/3p5/4r4/3K1p3 w - - 0 1 +go depth 10 +position fen 4kabC1/4a2P1/2N1n4/9/2N6/9/9/5n3/2pRp4/3K5 w - - 0 1 +go depth 10 +position fen 6b2/9/3k5/4P1N2/2b6/9/9/9/3p2p2/4K4 w - - 0 1 +go depth 10 +position fen 5ab2/4k1C2/5P3/9/9/6R2/3r2C2/5K3/4r4/3n5 w - - 0 1 +go depth 10 +position fen 5ab2/1P1k5/3a4b/9/6p2/9/6P2/9/4K4/5C3 w - - 0 1 +go depth 10 +position fen 9/5R3/3k5/2P6/9/9/6C2/1rn2p3/9/5K3 w - - 0 1 +go depth 10 +position fen 1C1k1a1N1/1P2a4/1n7/2n6/9/9/5R3/4R4/2r2p1r1/4K4 w - - 0 1 +go depth 10 +position fen 4ka3/c1r3n2/6N2/9/2r2N2R/9/9/1p7/1p1KC2n1/2p1cC3 w - - 0 1 +go depth 10 +position fen 3k2P2/9/3P5/4N4/4r4/4C4/9/2n3n2/2c3p2/4K4 w - - 0 1 +go depth 10 +position fen 3aka3/5PP2/2N6/4c4/9/R8/9/3p5/2p1r4/3K5 w - - 0 1 +go depth 10 +position fen 3k5/9/b8/9/9/rpp6/6R2/3ABA3/2r6/3K2R2 w - - 0 1 +go depth 10 +position fen 9/4k1PP1/9/9/9/9/9/3K1p3/4cn1C1/4r3R w - - 0 1 +go depth 10 +position fen 6R2/3P1k3/3a1a3/9/9/6B2/9/B1r6/5p3/4K4 w - - 0 1 +go depth 10 +position fen 4k4/4a4/4P4/9/9/9/9/4B4/9/4K4 w - - 0 1 +go depth 10 +position fen 6R2/5k3/5a3/5R3/6p2/9/9/4rC2B/2n6/5KBr1 w - - 0 1 +go depth 10 +position fen 6nC1/4n4/5k3/6PN1/9/5r2N/9/5r3/2p1p4/3K5 w - - 0 1 +go depth 10 +position fen 3P1k3/9/9/5P3/6c2/3p2C2/9/9/4p4/5K3 w - - 0 1 +go depth 10 +position fen 1n1a1k1P1/6P2/5a2c/6C2/9/9/9/9/1p2pp3/3K5 w - - 0 1 +go depth 10 +position fen 2ba1abR1/9/4k4/7N1/4r4/9/9/1p3p3/6p2/3K5 w - - 0 1 +go depth 10 +position fen 4c4/4ak3/5aP2/9/9/7R1/3r1r1RC/4p1n2/9/4K2c1 w - - 0 1 +go depth 10 +position fen 3ak4/4aPP2/4b4/2r1C2N1/9/3R5/9/9/3p1p3/4KArc1 w - - 0 1 +go depth 10 +position fen N1b1kab2/3Pa4/6n2/1R7/9/1C7/6r2/5K3/4r4/3n5 w - - 0 1 +go depth 10 +position fen 2Rak1b2/4aN3/9/7C1/8C/7r1/9/9/3p1pc2/4K3c w - - 0 1 +go depth 10 +position fen 9/5k3/5a3/p2N3r1/9/9/P8/7C1/9/5K3 w - - 0 1 +go depth 10 +position fen 2Pc5/r3a1c1R/5k3/6P2/2b3p2/9/9/9/3p1p3/4K4 w - - 0 1 +go depth 10 +position fen 4cknr1/3Ca2R1/2Ca2Rc1/4n4/9/9/9/9/4p4/3K1p3 w - - 0 1 +go depth 10 +position fen 1P7/4c4/5k3/6P2/3P5/1N7/6n2/4C4/2p1p4/3K5 w - - 0 1 +go depth 10 +position fen 9/4a3P/3ak4/9/2P1P4/3C5/P2p1c3/9/4K4/r1p2r3 w - - 0 1 +go depth 10 +position fen 2bak2rr/3Ra4/4b3N/4C4/9/9/9/2R6/3pAp3/4KAn1c w - - 0 1 +go depth 10 +position fen 4k4/1N1Pr1P2/5C3/5n3/9/9/9/9/3p3p1/1Cp2K3 w - - 0 1 +go depth 10 +position fen 9/4c1P2/3kb4/2C6/3P5/9/9/4BA3/4pnppc/2BKnrrpp w - - 0 1 +go depth 10 +position fen 4r4/2P6/3kb1P2/9/1n7/7N1/9/1cr1BK1C1/6p2/4R4 w - - 0 1 +go depth 10 +position fen 2P2R3/4r4/3k5/9/9/9/9/9/5K3/9 w - - 0 1 +go depth 10 +position fen 3r2b2/3kaP3/3rba3/C1n6/9/3RC4/9/9/2p1p4/3K5 w - - 0 1 +go depth 10 +position fen 3a5/4a4/4k4/9/4n3R/9/9/4rC3/1p2A4/3K5 w - - 0 1 +go depth 10 +position fen 4k4/9/9/4P4/4p4/8R/CRp1p4/3K5/1r7/5n3 w - - 0 1 +go depth 10 +position fen 3a1kb2/4a3C/4bC3/6N2/9/9/9/9/3r1p3/2p1K1p2 w - - 0 1 +go depth 10 +position fen 3ar1b2/4n4/3akN3/9/5P3/9/9/1p2p3R/5p3/4K4 w - - 0 1 +go depth 10 +position fen cr6R/1c2k4/2Pab4/n8/6b2/4N2N1/9/1R7/C2p1p3/2p1K1n1r w - - 0 1 +go depth 10 +position fen c8/5P3/3ak4/6r2/8N/9/9/9/pp2pp3/C2K5 w - - 0 1 +go depth 10 +position fen 3P1k1N1/1cC4R1/3nb4/4n4/9/9/9/7r1/3r1p3/4K4 w - - 0 1 +go depth 10 +position fen 5k2N/3r2P2/9/9/2p3bC1/9/9/9/2pp2p2/4K2p1 w - - 0 1 +go depth 10 +position fen 4k4/4a4/4b4/3R5/2b6/6P2/1r2Pp3/3K3CR/4r4/2Bc2B1n w - - 0 1 +go depth 10 +position fen 2bk5/r3aR3/n1r1b4/9/9/6R2/9/3n5/4p4/1C3K3 w - - 0 1 +go depth 10 +position fen 2b2a3/4a4/4k4/5PRc1/9/8N/4P4/3K5/2rpp4/9 w - - 0 1 +go depth 10 +position fen 1P1ak1b2/4a4/4b4/6p2/2N4N1/1C2C4/6P2/r2ppp3/9/c3KA3 w - - 0 1 +go depth 10 +position fen 3ak4/4a4/9/9/4R4/9/9/3AKA3/4C4/4r4 w - - 0 1 +go depth 10 +position fen 1r1a1a3/4k4/4bc3/cN1R1C3/2b6/9/9/9/4pp3/2BK2C2 w - - 0 1 +go depth 10 +position fen 2C3Pc1/3kaRC2/5a3/6N2/9/9/9/2r3R2/3p1pp2/4K1B2 w - - 0 1 +go depth 10 +position fen 1rb2k3/1R7/2n1b4/4p4/6R2/5CN2/9/9/4p2r1/3K5 w - - 0 1 +go depth 10 +position fen r2k1a3/1PP4R1/3a5/9/9/7CR/9/5n3/4r4/3K1c3 w - - 0 1 +go depth 10 +position fen 3k2bP1/4a4/3Pba3/8r/9/7N1/9/4C4/4p2r1/3K5 w - - 0 1 +go depth 10 +position fen 3a1a3/3r1k3/4b4/6P2/2b6/2B6/9/9/5C3/2B1KR1rc w - - 0 1 +go depth 10 +position fen 9/9/3ak4/9/4P4/9/6R1P/r2AB4/2r6/3K4R w - - 0 1 +go depth 10 +position fen 2bak4/4a2r1/4c4/CNrN5/8R/6C2/9/9/2p1pp3/3K5 w - - 0 1 +go depth 10 +position fen 4kc3/4aRN2/9/9/9/9/9/C8/2pr2pr1/4K4 w - - 0 1 +go depth 10 +position fen 1c3a3/4a4/3k1N3/9/4R4/C8/9/6n2/3r1r3/2p1K4 w - - 0 1 +go depth 10 +position fen N1Paka3/9/9/6N2/7n1/9/9/4C4/r2p1p3/4K4 w - - 0 1 +go depth 10 +position fen 3k5/2P6/2P3N2/P8/9/9/6p2/3n5/4p4/3K5 w - - 0 1 +go depth 10 +position fen 4ck3/4a4/9/4p4/9/9/9/4K4/Cn1rA4/3N1C3 w - - 0 1 +go depth 10 +position fen 4r4/6P2/C2aNk3/9/9/9/9/9/2p1p2r1/3K5 w - - 0 1 +go depth 10 +position fen 2b1k1b2/3P1P3/3a1a3/7C1/9/9/9/9/1p2pp3/3K5 w - - 0 1 +go depth 10 +position fen 3k5/9/9/2p6/9/4N4/9/9/9/4K4 w - - 0 1 +go depth 10 +position fen 3akr3/R4r2C/3a5/9/9/9/9/6n2/C2pA4/1R2K4 w - - 0 1 +go depth 10 +position fen 4k4/4a4/3r1a3/9/4R4/9/9/3A1A3/4K4/4C4 w - - 0 1 +go depth 10 +position fen 3ak2P1/4aP3/9/9/9/9/9/9/2r2rC2/c3K4 w - - 0 1 +go depth 10 +position fen 3a1k3/1Nc1a1R2/5rN2/9/9/9/9/9/3p5/4K1Rrc w - - 0 1 +go depth 10 +position fen 2rak4/4aP1P1/9/c3P4/6N2/9/5C3/9/3r5/4K4 w - - 0 1 +go depth 10 +position fen 5k3/3Pa4/5a3/8N/9/9/7p1/9/4K4/9 w - - 0 1 +go depth 10 +position fen 2baka3/1P7/4b4/7C1/p8/rp7/cp7/cp7/rp3K3/nn6C w - - 0 1 +go depth 10 +position fen 4k4/3P1P1P1/9/9/9/9/9/3p3p1/1C2pn3/3K5 w - - 0 1 +go depth 10 +position fen 2bak4/4a2R1/4b1R2/7C1/6C2/9/9/9/5pr2/4K2cr w - - 0 1 +go depth 10 +position fen 2Ra1k3/3ra4/6P2/9/8p/6R2/2p6/7r1/4K4/c2A5 w - - 0 1 +go depth 10 +position fen 3k5/9/4P4/9/9/5R3/9/9/5K3/4r1c2 w - - 0 1 +go depth 10 +position fen 3a1a3/3k3c1/1n2RN3/5r3/8R/9/2n6/9/3r1p3/2c1K4 w - - 0 1 +go depth 10 +position fen 4r2N1/3ka2c1/C3b1P2/4R1N2/2b6/9/4C4/9/4p1r2/5K3 w - - 0 1 +go depth 10 +position fen 1R1a5/2P1k4/1n2b4/4c4/9/9/9/2C5R/4r1r2/5K3 w - - 0 1 +go depth 10 +position fen 2bak1P2/2P1a3R/4b3N/1N7/9/9/9/2C1B1r1n/1p1rC4/2p1K1p2 w - - 0 1 +go depth 10 +position fen 9/9/5k3/2p1P1p2/9/5pB2/9/3A5/9/2B1Kc3 w - - 0 1 +go depth 10 +position fen 4ka2c/9/3a5/9/6rr1/1RR6/9/4p4/9/C3K4 w - - 0 1 +go depth 10 +position fen 3k5/4P4/5c3/c8/9/9/9/B8/3C5/5KC2 w - - 0 1 +go depth 10 +position fen 3k5/9/3a5/9/9/9/9/5R3/c2rA2p1/3C1K3 w - - 0 1 +go depth 10 +position fen 3a1k3/4a1P2/R3b1P2/4P4/4p4/9/9/5C3/4p1r2/3K5 w - - 0 1 +go depth 10 +position fen 1C7/1CRPak3/4ba3/9/2b6/9/5p3/9/3pr2p1/5K3 w - - 0 1 +go depth 10 +position fen 3k5/1PP1rnr2/9/9/4p4/2p1C4/4N4/9/7pc/2RC1K2c w - - 0 1 +go depth 10 +position fen 3aka3/5P3/2n3n2/C3N4/1N1R5/9/9/9/3p1r3/4KArc1 w - - 0 1 +go depth 10 +position fen 3akar2/9/7R1/5R3/3c5/9/9/9/9/3K5 w - - 0 1 +go depth 10 +position fen 2b6/1C2k4/n3bR3/6N2/9/9/9/1R7/3p2rr1/1C2K3c w - - 0 1 +go depth 10 +position fen 5a2C/3NakCR1/6Nc1/3r2P1p/7n1/9/9/9/3p1p3/4K2cr w - - 0 1 +go depth 10 +position fen 5kb2/1c2R4/n1n2P3/2P6/8C/5r1R1/9/9/2p1Cp3/3AKArc1 w - - 0 1 +go depth 10 +position fen r8/3ka2R1/3a3C1/1n2pN3/9/R2N1n2c/5c3/2r2A3/3p1p1p1/4K1C2 w - - 0 1 +go depth 10 +position fen 3k5/4a4/3aP3n/9/9/9/6R2/6R2/2rc1pr2/1c2K4 w - - 0 1 +go depth 10 +position fen 5k3/1N1P4R/3aba3/6P2/9/C2c5/4r4/9/4p4/3K1cr2 w - - 0 1 +go depth 10 +position fen 4ka3/6P2/3a5/1R4C2/4C4/9/9/9/pr2pp3/3K5 w - - 0 1 +go depth 10 +position fen 3k2b1R/4P4/4P3b/9/9/5Cn2/9/9/4p4/2p2K1pC w - - 0 1 +go depth 10 +position fen 3ak1b2/4a4/9/6pN1/4r1b2/9/9/2n6/3pA4/2p1KA1RC w - - 0 1 +go depth 10 +position fen n1ck1P3/4P2P1/4baP1b/5cP2/N8/3C1C3/5n3/5p3/R1r1p3r/3R1K3 w - - 0 1 +go depth 10 +position fen 4k3C/2P1aN3/4b3b/7R1/9/6B2/9/2n5B/1r1c1p3/4K1c1r w - - 0 1 +go depth 10 +position fen 2ba5/4ak3/bc4PN1/2p6/8p/p8/9/9/3p5/4K4 w - - 0 1 +go depth 10 +position fen 2b1ka3/3Pa4/4b1c2/6RC1/3R2c2/8C/9/2n6/3p3r1/4K4 w - - 0 1 +go depth 10 +position fen 1c2k1C2/c4P3/5a3/7R1/9/9/9/9/3rr2C1/5K3 w - - 0 1 +go depth 10 +position fen 3a5/4ak3/2R3C2/4P1N2/9/9/9/6n2/1n3p3/c1rRK4 w - - 0 1 +go depth 10 +position fen 3k4C/2N6/9/9/9/9/9/9/3p2p2/4K3n w - - 0 1 +go depth 10 +position fen 3a5/4a4/5k3/9/9/8C/9/2cc1C3/2rrN4/4K4 w - - 0 1 +go depth 10 +position fen 1Pcak4/9/3a3C1/4p4/9/9/5R3/4pR3/3pp1r2/3C1K3 w - - 0 1 +go depth 10 +position fen 3a1kb2/4a4/4b4/5NN2/9/9/9/8C/3r4c/4K1n2 w - - 0 1 +go depth 10 +position fen 3ak4/1P2a4/4b2R1/9/4r4/9/9/2p1CK2p/6p2/9 w - - 0 1 +go depth 10 +position fen 1rr2ab2/2RRak3/9/1N7/9/9/9/9/2p1p4/3K5 w - - 0 1 +go depth 10 +position fen C3ka3/4aP1P1/9/2P6/9/9/9/9/2p2n2c/3RK1pr1 w - - 0 1 +go depth 10 +position fen 3ak4/1C2aR3/2n1c4/7N1/9/7R1/9/4C4/4A1rnr/3AK3c w - - 0 1 +go depth 10 +position fen 3a2b2/3k4C/3a4b/6P2/9/9/9/9/9/4K4 w - - 0 1 +go depth 10 +position fen 3c1a3/8r/1NP1k4/5P3/5n3/9/R8/R4p3/C2Np1p2/1c3K3 w - - 0 1 +go depth 10 +position fen 1Cb1P1r2/3k5/c1Pab4/6P2/1N3r3/9/9/4c4/2p1p4/3K5 w - - 0 1 +go depth 10 +position fen 3ak1b2/4a4/4b4/1r7/4R3R/9/9/4B4/4A4/4K4 w - - 0 1 +go depth 10 +position fen 3k2b2/2PN5/4bC3/8C/9/5r3/9/5p3/3nr4/3p1K3 w - - 0 1 +go depth 10 +position fen 4ka3/4a4/4b1r2/2N6/C5pC1/3R5/3R5/4B2n1/1n2p1p2/cr1N1K2c w - - 0 1 +go depth 10 +position fen 3aka3/9/9/9/2R6/2C1P4/9/CR2c3r/3p1p3/2p1K4 w - - 0 1 +go depth 10 +position fen 2ba1a2N/4k4/4b3N/9/4n1n2/9/9/3K1p3/3cp4/7R1 w - - 0 1 +go depth 10 +position fen 9/4a4/3a1k3/4R1PN1/4p4/9/2C2r3/1cpAK4/3rAp3/2n6 w - - 0 1 +go depth 10 +position fen 3k5/9/9/8p/9/2R3B1r/9/4BA3/6r2/2R2K3 w - - 0 1 +go depth 10 +position fen n1b2ab2/3Pak2C/1N7/4RP3/9/4R4/9/2p1K1p2/4cr3/C3rn3 w - - 0 1 +go depth 10 +position fen 5k3/5c3/9/9/9/4c4/9/9/9/R2K5 w - - 0 1 +go depth 10 +position fen 3a4R/4ak1rn/9/4pC3/4cN2N/5C3/9/9/r2p1p3/4K4 w - - 0 1 +go depth 10 +position fen 3akc3/2P6/3a5/4p4/4c4/2R1P4/9/9/C1R1p1pn1/2p2K3 w - - 0 1 +go depth 10 +position fen 2Ra1kb2/R3c4/4b3r/9/9/9/1r5C1/9/4p4/3K5 w - - 0 1 +go depth 10 +position fen 4ka3/2P2P3/6N2/4n1R2/9/9/9/6pr1/4AK3/2rCcNpC1 w - - 0 1 +go depth 10 +position fen 5a3/9/3ak4/9/4P4/6B1R/9/4B4/3pr2p1/5K3 w - - 0 1 +go depth 10 +position fen 3P2C1R/4ak3/4b3b/9/2n5r/9/9/3C1p1R1/3p1r3/4K4 w - - 0 1 +go depth 10 +position fen 4ka3/4a2n1/3Rc4/9/9/9/2R1r4/4C4/2nr1p3/2p1K4 w - - 0 1 +go depth 10 +position fen 2bk1a1P1/4a3c/r1PcbN3/5CP2/3R2p2/2r6/4p4/5K3/7CR/3n5 w - - 0 1 +go depth 10 +position fen 4kab2/4a4/8b/9/9/9/9/9/9/4K1R2 w - - 0 1 +go depth 10 +position fen 4k4/3Pa4/5a3/9/8N/7p1/9/9/5K3/9 w - - 0 1 +go depth 10 +position fen 1N1a5/4a4/1NC2k3/4p1p2/9/2cp4R/7C1/1r2n3R/3pA4/r1c1K4 w - - 0 1 +go depth 10 +position fen 3a1k3/2NPac3/5PP2/4r1P2/4r4/9/9/2np5/3cnCp2/2pC1K3 w - - 0 1 +go depth 10 +position fen 2Pa5/3kaPn2/9/8R/9/C2C3N1/9/2pp1K1pp/7rr/7n1 w - - 0 1 +go depth 10 +position fen 2Rak1b2/4aPP2/4b4/1np2N3/3n5/1N2c4/1Cc6/3p1K3/4p4/6r2 w - - 0 1 +go depth 10 +position fen 3k1P1n1/4aPP2/NcCPPa2b/7nN/8r/9/9/4r4/3pCp3/4K4 w - - 0 1 +go depth 10 +position fen 5N3/4P4/PP1k5/n2r5/9/7C1/4r4/4p4/1p1Np2R1/3K5 w - - 0 1 +go depth 10 +position fen 5an2/4a4/3k5/2P1P3R/c3p4/7nR/9/3N2rC1/6pr1/3p1KcC1 w - - 0 1 +go depth 10 +position fen 3aN1RcC/4ak3/4b3n/5P3/9/9/9/2n2cC2/3p1p3/4KAr2 w - - 0 1 +go depth 10 +position fen 2b1k4/2PPa4/b1Ra5/9/9/1N7/5C1r1/2N2R3/3p1p3/cr1CKn1nc w - - 0 1 +go depth 10 +position fen c5b2/c1P1a4/2P1bk3/9/5P3/N8/6rr1/4CR3/4p4/5K3 w - - 0 1 +go depth 10 +position fen 5a1C1/3ka4/4b4/7N1/1Rb6/9/9/5n3/2r1p4/c4K3 w - - 0 1 +go depth 10 +position fen 2Ra5/2n1a4/4k4/5R1N1/NP1np4/9/9/9/6p2/r3cKBr1 w - - 0 1 +go depth 10 +position fen 3ak4/2PPa3N/9/2n6/9/2r6/9/cn1CKRrRc/5p3/7C1 w - - 0 1 +go depth 10 +position fen 1r1akab2/5R3/4b1n2/7N1/9/2B6/2r1P4/B2RC4/3KA4/c8 w - - 0 1 +go depth 10 +position fen C2k5/4P4/r2cb1N1b/8C/6r2/9/9/6p2/4p4/5K1R1 w - - 0 1 +go depth 10 +position fen 3k1P3/2P6/2Ca3N1/9/2b1p1b2/3n5/C8/9/2r1r4/3K1p3 w - - 0 1 +go depth 10 +position fen 4k2Pn/3Par3/2c1r3N/2p2n3/6N2/2c5C/1C7/R8/4pp3/3K5 w - - 0 1 +go depth 10 +position fen 5ab2/1P1k5/3a4b/2p6/6p2/5C3/6P2/9/9/2B1K4 w - - 0 1 +go depth 10 +position fen 1P3k3/3PP4/3P5/9/9/9/9/9/3ppC2p/5K3 w - - 0 1 +go depth 10 +position fen 3akab2/1r7/4b4/2C4N1/9/5p3/6R2/9/4p3c/3K5 w - - 0 1 +go depth 10 +position fen 2n2k1cr/3P4N/2n1b3b/2CR4R/2r3c1C/9/9/9/4p4/3K1p3 w - - 0 1 +go depth 10 +position fen 2bak3C/4a4/2P1b4/6RR1/9/9/9/9/c1nr1p3/4K4 w - - 0 1 +go depth 10 +position fen 3rk4/1R2a4/4ba3/9/4r4/9/7R1/4C4/3p5/1cBA1K3 w - - 0 1 +go depth 10 +position fen Ccbaka3/9/4b4/5N1n1/5R1C1/9/9/9/r2p1r3/4KR3 w - - 0 1 +go depth 10 +position fen 3k5/9/3a4n/9/6P2/9/9/5R3/c2rA2p1/3C1K3 w - - 0 1 +go depth 10 +position fen C4r3/3ka4/3acr3/1N1P4C/N2P5/3n5/9/3p5/4p4/3K5 w - - 0 1 +go depth 10 +position fen 1R1a1k3/2Cna1P2/b3b1N2/9/9/9/9/4K1R2/3r1r3/5n3 w - - 0 1 +go depth 10 +position fen 4k3P/3R5/3R5/9/9/9/3n5/5r3/3K5/2r6 w - - 0 1 +go depth 10 +position fen N3ka3/2PPa3C/4b1n1b/9/4N1C1R/9/r8/6n2/3p1r3/4K4 w - - 0 1 +go depth 10 +position fen 3k1a3/4a4/4P4/1R4C2/9/9/9/4B1r2/5p3/3AK2n1 w - - 0 1 +go depth 10 +position fen 5k3/9/9/9/9/9/6p2/4K1p2/6p2/C8 w - - 0 1 +go depth 10 +position fen 2ba2b1C/R1n2k3/9/1n3P3/9/8N/9/4R1p2/3p1p3/2p1K4 w - - 0 1 +go depth 10 +position fen 3a1k2r/1R2P4/3a5/2N6/9/9/9/2n1p4/3p1p3/RC2K1Crc w - - 0 1 +go depth 10 +position fen 2baka3/9/b4P3/9/9/4C4/9/9/9/3K5 w - - 0 1 +go depth 10 +position fen 4k4/4C4/5P3/1R7/9/9/4r4/3A5/6pp1/5K3 w - - 0 1 +go depth 10 +position fen 4k4/3P1P3/9/9/9/9/9/4C4/3p1p3/1pB1K4 w - - 0 1 +go depth 10 +position fen 3a1a3/5k3/9/5N3/9/9/7C1/3A3p1/4Ar3/4K1B2 w - - 0 1 +go depth 10 +position fen 4k4/4a3R/4Pa3/7C1/4p4/9/9/2r6/5r3/3K5 w - - 0 1 +go depth 10 +position fen 2b1kcPP1/4a3n/4ba3/4C1R2/5RN2/9/9/3n5/3r2r2/4K4 w - - 0 1 +go depth 10 +position fen 4ka3/2P2P3/3R1a3/4r2C1/9/9/6pC1/7p1/4r4/5K3 w - - 0 1 +go depth 10 +position fen 4ka3/3Pa4/7Cb/8p/9/9/9/9/1p5p1/4K4 w - - 0 1 +go depth 10 +position fen 2r1kab1r/3Ra4/4b4/1N7/3C5/9/9/1R2B4/3p1p3/c3K4 w - - 0 1 +go depth 10 +position fen 4kaN2/2CPa4/9/5P3/6RC1/6B2/7n1/2n6/3pA4/4KABrc w - - 0 1 +go depth 10 +position fen 7c1/3P3r1/r2ak1P2/6P2/9/9/7CR/2p1B1np1/1p2c2CR/4K4 w - - 0 1 +go depth 10 +position fen 9/4ak1P1/5a3/p8/9/9/9/BC1c5/8C/c3K4 w - - 0 1 +go depth 10 +position fen 2Paka1P1/7PC/n3b4/9/3N2b2/9/c1n6/3r5/1cpC4r/2BK3R1 w - - 0 1 +go depth 10 +position fen 2n1P4/c2kaRR2/3a3c1/3P5/2P6/5N3/r8/2nC5/4r4/3p1K3 w - - 0 1 +go depth 10 +position fen 2bk5/4a4/3Pb2R1/9/9/9/9/2p6/3K2p2/2C1rnp2 w - - 0 1 +go depth 10 +position fen 1RC1k1b2/3na2P1/3ab4/9/9/9/6P2/2n6/3p2rr1/2pCKR3 w - - 0 1 +go depth 10 +position fen 4k4/3P5/9/r1p6/4r4/9/2R1p4/4BK2B/1N4p2/3ARA2C w - - 0 1 +go depth 10 +position fen 2ba1k3/4a4/n3N4/9/9/9/9/3RB1r2/2p2r3/3AK2R1 w - - 0 1 +go depth 10 +position fen r3k4/3P3n1/3cb3b/3N5/CRR3p2/9/9/3r5/4p1p2/5K1C1 w - - 0 1 +go depth 10 +position fen 3a2b2/4a1P2/5k1C1/4P4/2p3b2/8p/9/9/4p4/3K5 w - - 0 1 +go depth 10 +position fen 3ak1b2/4arP2/5P2b/9/6P2/9/9/9/9/4K4 w - - 0 1 +go depth 10 +position fen 3ak1b1r/4a2Pn/4b4/4C4/9/9/cR7/n8/4A1p2/3AKC3 w - - 0 1 +go depth 10 +quit diff --git a/TEST/MAKEFILE.BAT b/TEST/MAKEFILE.BAT new file mode 100644 index 0000000..23ba6c2 --- /dev/null +++ b/TEST/MAKEFILE.BAT @@ -0,0 +1,5 @@ +@ECHO OFF +CL /DNDEBUG /O2 /W3 /Fe..\BIN\UNITTEST.EXE ..\ELEEYE\PREGEN.CPP ..\ELEEYE\POSITION.CPP ..\ELEEYE\GENMOVES.CPP UNITTEST.CPP +CL /DNDEBUG /O2 /W3 /Fe..\BIN\MAKETEST.EXE ..\ELEEYE\PREGEN.CPP ..\ELEEYE\POSITION.CPP ..\ELEEYE\GENMOVES.CPP ..\CCHESS\CCHESS.CPP ..\CCHESS\PGNFILE.CPP MAKETEST.CPP +CL /DNDEBUG /O2 /W3 /Fe..\BIN\UCCITEST.EXE ..\BASE\PIPE.CPP ..\ELEEYE\PREGEN.CPP ..\ELEEYE\POSITION.CPP ..\ELEEYE\GENMOVES.CPP UCCITEST.CPP SHLWAPI.LIB +DEL *.OBJ \ No newline at end of file diff --git a/TEST/MAKETEST.INI b/TEST/MAKETEST.INI new file mode 100644 index 0000000..1b7ab97 --- /dev/null +++ b/TEST/MAKETEST.INI @@ -0,0 +1,11 @@ +[UCCI-Test] +WinMove=On +DrawMove=On +LossMove=On +UnknownMove=On +Player=象眼竞技 +Player=相眼竞技 +SkipHead=20 +SkipTail=20 +Output=TESTPOS.TXT +Folder=C:\ \ No newline at end of file diff --git a/TEST/UCCITEST.INI b/TEST/UCCITEST.INI new file mode 100644 index 0000000..c4a5bcc --- /dev/null +++ b/TEST/UCCITEST.INI @@ -0,0 +1,10 @@ +[UCCI-Test] +Positions=TESTPOS.TXT +Engine=ELEEYE.EXE +Nodes=On +Init=setoption usebook off +Init=setoption promotion on +Command=go depth 6 +Reset=On +Timeout=60 +Output=RESULT.TXT \ No newline at end of file diff --git a/TEST/makefile.sh b/TEST/makefile.sh new file mode 100644 index 0000000..e18168b --- /dev/null +++ b/TEST/makefile.sh @@ -0,0 +1,3 @@ +g++ -DNDEBUG -O4 -Wall -oUNITTEST.EXE ../eleeye/pregen.cpp ../eleeye/position.cpp ../eleeye/genmoves.cpp unittest.cpp +g++ -DNDEBUG -O4 -Wall -oMAKETEST.EXE ../eleeye/pregen.cpp ../eleeye/position.cpp ../eleeye/genmoves.cpp ../cchess/cchess.cpp ../cchess/pgnfile.cpp maketest.cpp +g++ -DNDEBUG -O4 -Wall -oUCCITEST.EXE ../base/pipe.cpp ../eleeye/pregen.cpp ../eleeye/position.cpp ../eleeye/genmoves.cpp uccitest.cpp \ No newline at end of file diff --git a/TEST/maketest.cpp b/TEST/maketest.cpp new file mode 100644 index 0000000..e006eba --- /dev/null +++ b/TEST/maketest.cpp @@ -0,0 +1,285 @@ +/* +Test-Position Maker - for UCCI-Engines +Designed by Morning Yellow, Version: 3.1, Last Modified: Nov. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#ifdef _WIN32 + #include +#else + #include +#endif +#include "../base/base2.h" +#include "../base/parse.h" +#include "../eleeye/position.h" +#include "../cchess/cchess.h" +#include "../cchess/pgnfile.h" + +const int MAX_CHAR = 1024; +const int MAX_PLAYER = 16; + +static struct { + bool bWinMove, bDrawMove, bLossMove, bUnknownMove; + int nPlayerNum, nSkipHead, nSkipTail; + char szPlayerList[MAX_PLAYER][MAX_CHAR]; + FILE *fpOutput; +} MakeTest; + +static bool InPlayerList(const char *szPlayer) { + int i; + if (MakeTest.nPlayerNum == 0) { + return true; + } + for (i = 0; i < MakeTest.nPlayerNum; i ++) { + if (StrEqv(szPlayer, MakeTest.szPlayerList[i])) { + return true; + } + } + return false; +} + +static void BuildTestFromFile(const char *szFilePath) { + int i, mv; + bool bOutput[2]; + uint32_t dwMoveStr; + char szFen[128]; + char szFileName[MAX_CHAR]; + char *szResult, *lpSeperator; + PgnFileStruct pgn; + + if (!pgn.Read(szFilePath)) { + return; + } + if (pgn.nMaxMove <= MakeTest.nSkipHead + MakeTest.nSkipTail) { + return; + } + switch (pgn.nResult) { + case 1: + bOutput[0] = MakeTest.bWinMove; + bOutput[1] = MakeTest.bLossMove; + szResult = "1-0"; + break; + case 2: + bOutput[0] = MakeTest.bDrawMove; + bOutput[1] = MakeTest.bDrawMove; + szResult = "1/2-1/2"; + break; + case 3: + bOutput[0] = MakeTest.bLossMove; + bOutput[1] = MakeTest.bWinMove; + szResult = "0-1"; + break; + default: + bOutput[0] = MakeTest.bUnknownMove; + bOutput[1] = MakeTest.bUnknownMove; + szResult = "?-?"; + break; + } + bOutput[0] = bOutput[0] && InPlayerList(pgn.szRed); + bOutput[1] = bOutput[1] && InPlayerList(pgn.szBlack); + if (!bOutput[0] && !bOutput[1]) { + return; + } + lpSeperator = strrchr(szFilePath, PATH_SEPERATOR); + if (lpSeperator == NULL) { + strcpy(szFileName, szFilePath); + } else { + strcpy(szFileName, lpSeperator + 1); + } + fprintf(MakeTest.fpOutput, "; %s: %s%s %s %s%s\n", szFileName, + bOutput[0] ? "-> " : "", pgn.szRed, szResult, pgn.szBlack, bOutput[1] ? " <-" : ""); + for (i = 0; i < MakeTest.nSkipHead; i ++) { + mv = pgn.wmvMoveTable[i + 1]; + if (pgn.posStart.ucpcSquares[DST(mv)] == 0) { + pgn.posStart.MakeMove(mv); + } else { + pgn.posStart.MakeMove(mv); + pgn.posStart.SetIrrev(); + } + } + for (i = MakeTest.nSkipHead; i < pgn.nMaxMove - MakeTest.nSkipTail; i ++) { + mv = pgn.wmvMoveTable[i + 1]; + if (bOutput[pgn.posStart.sdPlayer]) { + dwMoveStr = MOVE_COORD(mv); + pgn.posStart.ToFen(szFen); + fprintf(MakeTest.fpOutput, "%.4s %s\n", (const char *) &dwMoveStr, szFen); + } + if (pgn.posStart.ucpcSquares[DST(mv)] == 0) { + pgn.posStart.MakeMove(mv); + } else { + pgn.posStart.MakeMove(mv); + pgn.posStart.SetIrrev(); + } + } +} + +#ifdef _WIN32 + +static void SearchFolder(const char *szFolderPath); + +static void SearchFile(const char *szFilePath, const WIN32_FIND_DATA &wfd) { + if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + if (strlen(szFilePath) > 4) { + if (strnicmp(szFilePath + strlen(szFilePath) - 4, ".PGN", 4) == 0) { + BuildTestFromFile(szFilePath); + } + } + } else { + if (strcmp(wfd.cFileName, ".") != 0 && strcmp(wfd.cFileName, "..") != 0) { + SearchFolder(szFilePath); + } + } +} + +static void SearchFolder(const char *szFolderPath) { + char szFilePath[MAX_CHAR]; + WIN32_FIND_DATA wfd; + HANDLE hFind; + char *lpFilePath; + + strcpy(szFilePath, szFolderPath); + lpFilePath = szFilePath + strlen(szFolderPath); + if (*(lpFilePath - 1) == '\\') { + lpFilePath --; + } + strcpy(lpFilePath, "\\*"); + lpFilePath ++; + hFind = FindFirstFile(szFilePath, &wfd); + if (hFind != INVALID_HANDLE_VALUE) { + strcpy(lpFilePath, wfd.cFileName); + SearchFile(szFilePath, wfd); + while (FindNextFile(hFind, &wfd)) { + strcpy(lpFilePath, wfd.cFileName); + SearchFile(szFilePath, wfd); + } + } +} + +#else + +static void SearchFolder(const char *szFolderPath); + +static void SearchFile(const char *szFilePath, const dirent *lpdir) { + if (false) { + } else if (lpdir->d_type == DT_REG) { + if (strlen(szFilePath) > 4) { + if (strncasecmp(szFilePath + strlen(szFilePath) - 4, ".PGN", 4) == 0) { + BuildTestFromFile(szFilePath); + } + } + } else if (lpdir->d_type == DT_DIR) { + if (strcmp(lpdir->d_name, ".") != 0 && strcmp(lpdir->d_name, "..") != 0) { + SearchFolder(szFilePath); + } + } +} + +static void SearchFolder(const char *szFolderPath) { + char szFilePath[MAX_CHAR]; + DIR *dp; + dirent *lpdir; + char *lpFilePath; + + strcpy(szFilePath, szFolderPath); + lpFilePath = szFilePath + strlen(szFolderPath); + if (*(lpFilePath - 1) != '/') { + strcpy(lpFilePath, "/"); + lpFilePath ++; + } + dp = opendir(szFilePath); + if (dp != NULL) { + while ((lpdir = readdir(dp)) != NULL) { + strcpy(lpFilePath, lpdir->d_name); + SearchFile(szFilePath, lpdir); + } + closedir(dp); + } +} + +#endif + +int main(void) { + char szIniFile[MAX_CHAR], szLineStr[MAX_CHAR]; + char szOutput[MAX_CHAR], szFolder[MAX_CHAR]; + char *lp; + FILE *fpIniFile; + + LocatePath(szIniFile, "MAKETEST.INI"); + fpIniFile = fopen(szIniFile, "rt"); + if (fpIniFile == NULL) { + printf("%s: File Opening Error!\n", szIniFile); + return 0; + } + MakeTest.bUnknownMove = MakeTest.bWinMove = MakeTest.bDrawMove = MakeTest.bLossMove = false; + MakeTest.nPlayerNum = MakeTest.nSkipHead = MakeTest.nSkipTail = 0; + MakeTest.fpOutput = stdout; + szOutput[0] = '\0'; + strcpy(szFolder, "."); + while (fgets(szLineStr, MAX_CHAR, fpIniFile) != NULL) { + StrCutCrLf(szLineStr); + lp = szLineStr; + if (false) { + } else if (StrEqvSkip(lp, "WinMove=On")) { + MakeTest.bWinMove = true; + } else if (StrEqvSkip(lp, "WinMove=True")) { + MakeTest.bWinMove = true; + } else if (StrEqvSkip(lp, "DrawMove=On")) { + MakeTest.bDrawMove = true; + } else if (StrEqvSkip(lp, "DrawMove=True")) { + MakeTest.bDrawMove = true; + } else if (StrEqvSkip(lp, "LossMove=On")) { + MakeTest.bLossMove = true; + } else if (StrEqvSkip(lp, "LossMove=True")) { + MakeTest.bLossMove = true; + } else if (StrEqvSkip(lp, "UnknownMove=On")) { + MakeTest.bUnknownMove = true; + } else if (StrEqvSkip(lp, "UnknownMove=True")) { + MakeTest.bUnknownMove = true; + + } else if (StrEqvSkip(lp, "Player=")) { + if (MakeTest.nPlayerNum < MAX_PLAYER) { + strcpy(MakeTest.szPlayerList[MakeTest.nPlayerNum], lp); + MakeTest.nPlayerNum ++; + } + } else if (StrEqvSkip(lp, "SkipHead=")) { + MakeTest.nSkipHead = Str2Digit(lp, 0, 100); + } else if (StrEqvSkip(lp, "SkipTail=")) { + MakeTest.nSkipTail = Str2Digit(lp, 0, 100); + } else if (StrEqvSkip(lp, "Output=")) { + LocatePath(szOutput, lp); + } else if (StrEqvSkip(lp, "Folder=")) { + LocatePath(szFolder, lp); + } + } + fclose(fpIniFile); + + PreGenInit(); + ChineseInit(); + if (szOutput[0] != '\0') { + MakeTest.fpOutput = fopen(szOutput, "wt"); + if (MakeTest.fpOutput == NULL) { + MakeTest.fpOutput = stdout; + } + } + SearchFolder(szFolder); + if (MakeTest.fpOutput != stdout) { + fclose(MakeTest.fpOutput); + } + return 0; +} diff --git a/TEST/uccitest.cpp b/TEST/uccitest.cpp new file mode 100644 index 0000000..739778f --- /dev/null +++ b/TEST/uccitest.cpp @@ -0,0 +1,234 @@ +/* +UCCI-Engine Test Driver - for UCCI Engines +Designed by Morning Yellow, Version: 3.1, Last Modified: Nov. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include "../base/base2.h" +#include "../base/parse.h" +#include "../base/pipe.h" +#include "../eleeye/position.h" + +const int MAX_CHAR = 1024; // 配置文件的最大长度 +const int MAX_INIT = 16; // Init配置项最大行数 + +inline void GetMoveNodes(int &mvTest, int &nNodes, PipeStruct &pipe) { + char szLineStr[MAX_CHAR]; + char *lp; + if (pipe.LineInput(szLineStr)) { + lp = szLineStr; + if (StrEqvSkip(lp, "bestmove ")) { + mvTest = COORD_MOVE(*(uint32_t *) lp); + } else if (StrScanSkip(lp, " nodes ")) { + nNodes = Str2Digit(lp, 0, 2000000000); + } + } else { + Idle(); + } +} + +int main(void) { + int nInitNum, nTimeout; + bool bNodes, bReset; + char szIniFile[MAX_CHAR], szPosFile[MAX_CHAR], szEngineFile[MAX_CHAR]; + char szCommand[MAX_CHAR], szOutput[MAX_CHAR]; + char szInit[MAX_INIT][MAX_CHAR]; + + FILE *fpIniFile, *fpPosFile, *fpOutput; + char szLineStr[MAX_CHAR]; + char szFen[128]; + char *lp; + + PipeStruct pipe; + bool bUcciOkay; + int i, nHitNum, nNodes, nNodesTotal, mvBase, mvTest; + uint32_t dwMoveBase, dwMoveTest; + PositionStruct pos; + int64_t llTime; + + // 读取配置文件 + nInitNum = nTimeout = 0; + bNodes = bReset = false; + szPosFile[0] = szEngineFile[0] = szCommand[0] = szOutput[0] = '\0'; + LocatePath(szIniFile, "UCCITEST.INI"); + fpIniFile = fopen(szIniFile, "rt"); + if (fpIniFile == NULL) { + printf("%s: File Opening Error!\n", szIniFile); + return 0; + } + while (fgets(szLineStr, MAX_CHAR, fpIniFile) != NULL) { + StrCutCrLf(szLineStr); + lp = szLineStr; + if (false) { + } else if (StrEqvSkip(lp, "Positions=")) { + LocatePath(szPosFile, lp); + } else if (StrEqvSkip(lp, "Engine=")) { + LocatePath(szEngineFile, lp); + } else if (StrEqvSkip(lp, "Nodes=On")) { + bNodes = true; + } else if (StrEqvSkip(lp, "Nodes=True")) { + bNodes = true; + } else if (StrEqvSkip(lp, "Init=")) { + if (nInitNum < MAX_INIT) { + strcpy(szInit[nInitNum], lp); + nInitNum ++; + } + } else if (StrEqvSkip(lp, "Command=")) { + strcpy(szCommand, lp); + } else if (StrEqvSkip(lp, "Output=")) { + LocatePath(szOutput, lp); + } else if (StrEqvSkip(lp, "Reset=On")) { + bReset = true; + } else if (StrEqvSkip(lp, "Reset=True")) { + bReset = true; + } else if (StrEqvSkip(lp, "Timeout=")) { + nTimeout = Str2Digit(lp, 0, 3600); + } + } + fclose(fpIniFile); + nTimeout *= 1000; + + // 初始化引擎 + pipe.Open(szEngineFile); + pipe.LineOutput("ucci"); + llTime = GetTime(); + bUcciOkay = false; + while (!bUcciOkay && (int) (GetTime() - llTime) < 10000) { + if (pipe.LineInput(szLineStr)) { + if (StrEqv(szLineStr, "ucciok")) { + bUcciOkay = true; + } + } else { + Idle(); + } + } + if (!bUcciOkay) { + pipe.LineOutput("quit"); + printf("%s: Not a UCCI-Engine!\n", szEngineFile); + return 0; + } + for (i = 0; i < nInitNum; i ++) { + pipe.LineOutput(szInit[i]); + } + + // 读取测试局面文件 + fpPosFile = fopen(szPosFile, "rt"); + if (fpPosFile == NULL) { + printf("%s: File Opening Error!\n", szPosFile); + return 0; + } + + // 打开输出文件 + fpOutput = stdout; + if (szOutput[0] != '\0') { + fpOutput = fopen(szOutput, "wt"); + if (fpOutput == NULL) { + fpOutput = stdout; + } + } + PreGenInit(); + nNodesTotal = 0; + nHitNum = 0; + if (bNodes) { + fprintf(fpOutput, " No Base Test Result Nodes Position (FEN)\n"); + } else { + fprintf(fpOutput, " No Base Test Result Position (FEN)\n"); + } + fprintf(fpOutput, "===================================================\n"); + fflush(fpOutput); + i = 0; + while (fgets(szLineStr, MAX_CHAR, fpPosFile) != NULL) { + StrCutCrLf(szLineStr); + if (szLineStr[0] == ';') { + continue; + } + i ++; + // 读取局面 + dwMoveBase = *(uint32_t *) szLineStr; + mvBase = COORD_MOVE(dwMoveBase); + pos.FromFen(szLineStr + 5); + // 向引擎发送指令 + if (bReset) { + pipe.LineOutput("setoption newgame"); + } + pos.ToFen(szFen); + sprintf(szLineStr, "position fen %s - - 0 1", szFen); + pipe.LineOutput(szLineStr); + pipe.LineOutput(szCommand); + // 等待引擎返回结果 + mvTest = 0; + llTime = GetTime(); + while (mvTest == 0 && (nTimeout == 0 || (int) (GetTime() - llTime) < nTimeout)) { + GetMoveNodes(mvTest, nNodes, pipe); + } + // 如果超时仍然没有结果,则强行让引擎给出着法 + if (mvTest == 0) { + pipe.LineOutput("stop"); + llTime = GetTime(); + while (mvTest == 0 && (int) (GetTime() - llTime) < 1000) { + GetMoveNodes(mvTest, nNodes, pipe); + } + } + // 统计结果 + nNodesTotal += nNodes; + if (mvTest == 0) { + if (bNodes) { + fprintf(fpOutput, "%6d %.4s ---- Miss %10d %s\n", i, (const char *) &dwMoveBase, nNodes, szFen); + } else { + fprintf(fpOutput, "%6d %.4s ---- Miss %s\n", i, (const char *) &dwMoveBase, szFen); + } + } else { + dwMoveTest = MOVE_COORD(mvTest); + if (bNodes) { + fprintf(fpOutput, "%6d %.4s %.4s %s %10d %s\n", i, (const char *) &dwMoveBase, (const char *) &dwMoveTest, + mvTest == mvBase ? "Hit " : "Miss", nNodes, szFen); + } else { + fprintf(fpOutput, "%6d %.4s %.4s %s %s\n", i, (const char *) &dwMoveBase, (const char *) &dwMoveTest, + mvTest == mvBase ? "Hit " : "Miss", szFen); + } + nHitNum += (mvTest == mvBase ? 1 : 0); + } + fflush(fpOutput); + } + fprintf(fpOutput, "==================================================\n"); + if (bNodes) { + fprintf(fpOutput, " Total %6d Hits %10d\n", nHitNum, nNodesTotal); + } else { + fprintf(fpOutput, " Total %6d Hits\n", nHitNum); + } + fflush(fpOutput); + // 关闭输出文件 + if (fpOutput != stdout) { + fclose(fpOutput); + } + fclose(fpPosFile); + + // 关闭引擎 + pipe.LineOutput("quit"); + while (bUcciOkay && (int) (GetTime() - llTime) < 10000) { + if (pipe.LineInput(szLineStr)) { + if (StrEqv(szLineStr, "bye")) { + bUcciOkay = false; + } + } else { + Idle(); + } + } + return 0; +} diff --git a/TEST/unittest.cpp b/TEST/unittest.cpp new file mode 100644 index 0000000..b47e00a --- /dev/null +++ b/TEST/unittest.cpp @@ -0,0 +1,83 @@ +/* +Unit Test - for ElephantEye +Designed by Morning Yellow, Version: 3.12, Last Modified: Dec. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "../eleeye/position.h" + +const int MAX_CHAR = 1024; + +int main(char argc, char **argv) { + char szLineStr[MAX_CHAR]; + MoveStruct mvs[MAX_GEN_MOVES]; + PositionStruct pos; + FILE *fp; + int nPosNum, sqSrc, sqDst, i; + int nThisLegal, nThisGened, nThisMoved, nThisCheck; + int nTotalLegal, nTotalGened, nTotalMoved, nTotalCheck; + + if (argc <= 1) { + printf("=== ElephantEye Unit Test Program ===\n"); + printf("Usage: UNITTEST EPD-File\n"); + return 0; + } + fp = fopen(argv[1], "rt"); + if (fp == NULL) { + printf("%s: File Opening Error!\n", argv[1]); + } + PreGenInit(); + nPosNum = nTotalLegal = nTotalGened = nTotalMoved = nTotalCheck = 0; + printf(" No Legal Gened Moved Check Position (FEN)\n"); + printf("===================================================\n"); + fflush(stdout); + while (fgets(szLineStr, MAX_CHAR, fp) != NULL) { + nPosNum ++; + nThisLegal = nThisMoved = nThisCheck = 0; + pos.FromFen(szLineStr); + for (sqSrc = 0; sqSrc < 256; sqSrc ++) { + if (IN_BOARD(sqSrc)) { + for (sqDst = 0; sqDst < 256; sqDst ++) { + if (IN_BOARD(sqDst) && sqDst != sqSrc) { + nThisLegal += (pos.LegalMove(MOVE(sqSrc, sqDst)) ? 1 : 0); + } + } + } + } + nThisGened = pos.GenAllMoves(mvs); + for (i = 0; i < nThisGened; i ++) { + if (pos.MakeMove(mvs[i].wmv)) { + nThisMoved ++; + nThisCheck += (pos.LastMove().ChkChs > 0 ? 1 : 0); + pos.UndoMakeMove(); + } + } + pos.ToFen(szLineStr); + printf("%6d%6d%6d%6d%6d %s\n", nPosNum, nThisLegal, nThisGened, nThisMoved, nThisCheck, szLineStr); + fflush(stdout); + nTotalLegal += nThisLegal; + nTotalGened += nThisGened; + nTotalMoved += nThisMoved; + nTotalCheck += nThisCheck; + } + printf("====================================================\n"); + printf(" Total%6d%6d%6d%6d\n", nTotalLegal, nTotalGened, nTotalMoved, nTotalCheck); + fflush(stdout); + fclose(fp); + return 0; +} diff --git a/TREE/POSITION.FRM b/TREE/POSITION.FRM new file mode 100644 index 0000000..9bb414b --- /dev/null +++ b/TREE/POSITION.FRM @@ -0,0 +1,1369 @@ +VERSION 5.00 +Begin VB.Form frmPosition + BorderStyle = 4 'Fixed ToolWindow + Caption = "局面比较" + ClientHeight = 3240 + ClientLeft = 45 + ClientTop = 270 + ClientWidth = 5160 + ControlBox = 0 'False + BeginProperty Font + Name = "宋体" + Size = 9 + Charset = 134 + Weight = 400 + Underline = 0 'False + Italic = 0 'False + Strikethrough = 0 'False + EndProperty + LinkTopic = "Form1" + MaxButton = 0 'False + MinButton = 0 'False + ScaleHeight = 3240 + ScaleWidth = 5160 + ShowInTaskbar = 0 'False + StartUpPosition = 3 'Windows Default + Begin VB.CommandButton btnCopyFen2 + Caption = "复制FEN" + Height = 375 + Left = 3600 + TabIndex = 3 + Top = 0 + Width = 1095 + End + Begin VB.CommandButton btnCopyFen + Caption = "复制FEN" + Height = 375 + Left = 1080 + TabIndex = 2 + Top = 0 + Width = 1095 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 89 + Left = 4680 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 88 + Left = 4440 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 87 + Left = 4200 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 86 + Left = 3960 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 85 + Left = 3720 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 84 + Left = 3480 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 83 + Left = 3240 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 82 + Left = 3000 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 81 + Left = 2760 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 80 + Left = 4680 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 79 + Left = 4440 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 78 + Left = 4200 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 77 + Left = 3960 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 76 + Left = 3720 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 75 + Left = 3480 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 74 + Left = 3240 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 73 + Left = 3000 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 72 + Left = 2760 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 71 + Left = 4680 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 70 + Left = 4440 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 69 + Left = 4200 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 68 + Left = 3960 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 67 + Left = 3720 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 66 + Left = 3480 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 65 + Left = 3240 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 64 + Left = 3000 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 63 + Left = 2760 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 62 + Left = 4680 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 61 + Left = 4440 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 60 + Left = 4200 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 59 + Left = 3960 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 58 + Left = 3720 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 57 + Left = 3480 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 56 + Left = 3240 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 55 + Left = 3000 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 54 + Left = 2760 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 53 + Left = 4680 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 52 + Left = 4440 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 51 + Left = 4200 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 50 + Left = 3960 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 49 + Left = 3720 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 48 + Left = 3480 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 47 + Left = 3240 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 46 + Left = 3000 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 45 + Left = 2760 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 44 + Left = 4680 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 43 + Left = 4440 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 42 + Left = 4200 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 41 + Left = 3960 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 40 + Left = 3720 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 39 + Left = 3480 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 38 + Left = 3240 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 37 + Left = 3000 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 36 + Left = 2760 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 35 + Left = 4680 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 34 + Left = 4440 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 33 + Left = 4200 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 32 + Left = 3960 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 31 + Left = 3720 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 30 + Left = 3480 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 29 + Left = 3240 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 28 + Left = 3000 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 27 + Left = 2760 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 26 + Left = 4680 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 25 + Left = 4440 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 24 + Left = 4200 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 23 + Left = 3960 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 22 + Left = 3720 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 21 + Left = 3480 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 20 + Left = 3240 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 19 + Left = 3000 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 18 + Left = 2760 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 17 + Left = 4680 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 16 + Left = 4440 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 15 + Left = 4200 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 14 + Left = 3960 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 13 + Left = 3720 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 12 + Left = 3480 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 11 + Left = 3240 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 10 + Left = 3000 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 9 + Left = 2760 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 8 + Left = 4680 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 7 + Left = 4440 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 6 + Left = 4200 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 5 + Left = 3960 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 4 + Left = 3720 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 3 + Left = 3480 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 2 + Left = 3240 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 1 + Left = 3000 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares2 + Height = 255 + Index = 0 + Left = 2760 + Top = 600 + Width = 255 + End + Begin VB.Label lblPos2 + Caption = "当前局面" + Height = 255 + Left = 2760 + TabIndex = 1 + Top = 120 + Width = 855 + End + Begin VB.Label lblPos + Caption = "根局面" + Height = 255 + Left = 240 + TabIndex = 0 + Top = 120 + Width = 615 + End + Begin VB.Image imgBoard2 + Height = 2655 + Left = 2640 + Picture = "POSITION.frx":0000 + Stretch = -1 'True + Top = 480 + Width = 2415 + End + Begin VB.Image imgSquares + Height = 255 + Index = 0 + Left = 240 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 1 + Left = 480 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 2 + Left = 720 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 3 + Left = 960 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 4 + Left = 1200 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 5 + Left = 1440 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 6 + Left = 1680 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 7 + Left = 1920 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 8 + Left = 2160 + Top = 600 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 9 + Left = 240 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 10 + Left = 480 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 11 + Left = 720 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 12 + Left = 960 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 13 + Left = 1200 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 14 + Left = 1440 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 15 + Left = 1680 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 16 + Left = 1920 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 17 + Left = 2160 + Top = 840 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 18 + Left = 240 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 19 + Left = 480 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 20 + Left = 720 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 21 + Left = 960 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 22 + Left = 1200 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 23 + Left = 1440 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 24 + Left = 1680 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 25 + Left = 1920 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 26 + Left = 2160 + Top = 1080 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 27 + Left = 240 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 28 + Left = 480 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 29 + Left = 720 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 30 + Left = 960 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 31 + Left = 1200 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 32 + Left = 1440 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 33 + Left = 1680 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 34 + Left = 1920 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 35 + Left = 2160 + Top = 1320 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 36 + Left = 240 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 37 + Left = 480 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 38 + Left = 720 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 39 + Left = 960 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 40 + Left = 1200 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 41 + Left = 1440 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 42 + Left = 1680 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 43 + Left = 1920 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 44 + Left = 2160 + Top = 1560 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 45 + Left = 240 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 46 + Left = 480 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 47 + Left = 720 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 48 + Left = 960 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 49 + Left = 1200 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 50 + Left = 1440 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 51 + Left = 1680 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 52 + Left = 1920 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 53 + Left = 2160 + Top = 1800 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 54 + Left = 240 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 55 + Left = 480 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 56 + Left = 720 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 57 + Left = 960 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 58 + Left = 1200 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 59 + Left = 1440 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 60 + Left = 1680 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 61 + Left = 1920 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 62 + Left = 2160 + Top = 2040 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 63 + Left = 240 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 64 + Left = 480 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 65 + Left = 720 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 66 + Left = 960 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 67 + Left = 1200 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 68 + Left = 1440 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 69 + Left = 1680 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 70 + Left = 1920 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 71 + Left = 2160 + Top = 2280 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 72 + Left = 240 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 73 + Left = 480 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 74 + Left = 720 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 75 + Left = 960 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 76 + Left = 1200 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 77 + Left = 1440 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 78 + Left = 1680 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 79 + Left = 1920 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 80 + Left = 2160 + Top = 2520 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 81 + Left = 240 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 82 + Left = 480 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 83 + Left = 720 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 84 + Left = 960 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 85 + Left = 1200 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 86 + Left = 1440 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 87 + Left = 1680 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 88 + Left = 1920 + Top = 2760 + Width = 255 + End + Begin VB.Image imgSquares + Height = 255 + Index = 89 + Left = 2160 + Top = 2760 + Width = 255 + End + Begin VB.Image imgBoard + Height = 2655 + Left = 120 + Picture = "POSITION.frx":4359 + Stretch = -1 'True + Top = 480 + Width = 2415 + End +End +Attribute VB_Name = "frmPosition" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False +' Search Tree Analyst - for UCCI Engines +' Designed by Morning Yellow, Version: 2.35, Last Modified: Oct. 2007 +' Copyright (C) 2004-2007 www.elephantbase.net +' +' This library is free software; you can redistribute it and/or +' modify it under the terms of the GNU Lesser General Public +' License as published by the Free Software Foundation; either +' version 2.1 of the License, or (at your option) any later version. +' +' This library 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 +' Lesser General Public License for more details. +' +' You should have received a copy of the GNU Lesser General Public +' License along with this library; if not, write to the Free Software +' Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Private Sub btnCopyFen_Click() + +Clipboard.Clear +Clipboard.SetText MkBStr(CchessBoard2Fen(App_posRoot)) + " - - 0 1" + +End Sub + +Private Sub btnCopyFen2_Click() + +Clipboard.Clear +Clipboard.SetText App_szCurrFen + " - - 0 1" + +End Sub diff --git a/TREE/POSITION.FRX b/TREE/POSITION.FRX new file mode 100644 index 0000000..fcbc8f3 Binary files /dev/null and b/TREE/POSITION.FRX differ diff --git a/TREE/RES/ALPHA.ICO b/TREE/RES/ALPHA.ICO new file mode 100644 index 0000000..9a0ca3f Binary files /dev/null and b/TREE/RES/ALPHA.ICO differ diff --git a/TREE/RES/BETA.ICO b/TREE/RES/BETA.ICO new file mode 100644 index 0000000..fcd57ca Binary files /dev/null and b/TREE/RES/BETA.ICO differ diff --git a/TREE/RES/LEAF.ICO b/TREE/RES/LEAF.ICO new file mode 100644 index 0000000..963b482 Binary files /dev/null and b/TREE/RES/LEAF.ICO differ diff --git a/TREE/RES/NONE.ICO b/TREE/RES/NONE.ICO new file mode 100644 index 0000000..896a75b Binary files /dev/null and b/TREE/RES/NONE.ICO differ diff --git a/TREE/RES/PV.ICO b/TREE/RES/PV.ICO new file mode 100644 index 0000000..af96316 Binary files /dev/null and b/TREE/RES/PV.ICO differ diff --git a/TREE/UCCITREE.BAS b/TREE/UCCITREE.BAS new file mode 100644 index 0000000..d8d5c50 --- /dev/null +++ b/TREE/UCCITREE.BAS @@ -0,0 +1,300 @@ +Attribute VB_Name = "mdlUcciTree" +' Search Tree Analyst - for UCCI Engines +' Designed by Morning Yellow, Version: 3.1, Last Modified: Nov. 2007 +' Copyright (C) 2004-2007 www.elephantbase.net +' +' This library is free software; you can redistribute it and/or +' modify it under the terms of the GNU Lesser General Public +' License as published by the Free Software Foundation; either +' version 2.1 of the License, or (at your option) any later version. +' +' This library 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 +' Lesser General Public License for more details. +' +' You should have received a copy of the GNU Lesser General Public +' License along with this library; if not, write to the Free Software +' Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Option Explicit + +Public Const MAX_NODE_NUM As Integer = 32767 + +Public Const NODE_NONE As Integer = 1 +Public Const NODE_LEAF As Integer = 2 +Public Const NODE_ALPHA As Integer = 3 +Public Const NODE_BETA As Integer = 4 +Public Const NODE_PV As Integer = 5 + +Public Const VALUE_NONE As Long = 0 +Public Const VALUE_LEAF_1 As Long = 1000000 +Public Const VALUE_LEAF_2 As Long = 2000000 +Public Const VALUE_ALPHA As Long = 3000000 +Public Const VALUE_BETA As Long = 4000000 +Public Const VALUE_PV_1 As Long = 5000000 +Public Const VALUE_PV_2 As Long = 6000000 +Public Const VALUE_BEST As Long = 7000000 + +Public Type NodeStruct + szFen As String + mvBest As Long + bExp As Boolean +End Type + +Public Type InfoStruct + szFen As String + mvBest As Long + bExp As Boolean + nIcon As Integer + vl As Long + szInfo As String +End Type + +Public App_bRunning As Boolean +Public App_pipe As PipeStruct +Public App_posRoot As PositionStruct +Public App_szCurrFen As String +Public App_nNodeNum As Integer +Public App_Nodes(0 To MAX_NODE_NUM) As NodeStruct + +Public Function Int2Str(ByVal n As Integer) + +Int2Str = LTrim(Str(n)) + +End Function + +Public Sub PipeOut(ByVal sz As String) + +PipeLineOutput App_pipe, sz + +End Sub + +Public Function PipeIn() As String + +Dim lpStr As Long, sz As String +lpStr = PipeLineInput(App_pipe) +If lpStr = 0 Then + PipeIn = "" +Else + sz = MkBStr(lpStr) + PipeIn = sz +End If + +End Function + +Public Sub ProbeHash(ByRef mvBest As Long, ByRef nAlphaDepth As Integer, ByRef nBetaDepth As Integer, ByRef vlAlpha As Integer, ByRef vlBeta As Integer, ByVal szFen As String) + +Dim szRecv As String, i As Integer +Dim dfThisTime As Double, dfLastTime As Double + +mvBest = 0 +nAlphaDepth = -1 +nBetaDepth = -1 +PipeOut "probe fen " + szFen + " - - 0 1" +dfLastTime = Timer +Do + szRecv = PipeIn + If szRecv = "" Then + Sleep 1 + dfThisTime = Timer + dfThisTime = dfThisTime + IIf(dfThisTime < dfLastTime, 86400#, 0#) + If dfThisTime > dfLastTime + 1# Then + Exit Do + End If + Else + If Left(szRecv, 7) = "pophash" Then + ' 获取mvBest + i = InStr(szRecv, " bestmove ") + If i > 0 Then + mvBest = Coord2Move(Mid(szRecv, i + 10, 4)) + End If + ' 获取nAlphaDepth和vlAlpha + i = InStr(szRecv, " lowerbound ") + If i > 0 Then + vlAlpha = Str2Int(Mid(szRecv, i + 12)) + i = InStr(i + 12, szRecv, " depth ") + If i > 0 Then + nAlphaDepth = Str2Int(Mid(szRecv, i + 7)) + End If + End If + ' 获取nBetaDepth和vlBeta + i = InStr(szRecv, " upperbound ") + If i > 0 Then + vlBeta = Str2Int(Mid(szRecv, i + 12)) + i = InStr(i + 12, szRecv, " depth ") + If i > 0 Then + nBetaDepth = Str2Int(Mid(szRecv, i + 7)) + End If + End If + Exit Do + End If + End If +Loop + +End Sub + +Public Sub AddNode(ByRef pos As PositionStruct, ByVal mvBest As Long, Optional ByVal nIndex As Integer = -1) + +Dim mvs(0 To 127) As Long +Dim Infos(0 To 127) As InfoStruct, InfoTemp As InfoStruct +Dim nTotal As Integer, nLegal As Integer, i As Integer, j As Integer +Dim nAlphaDepth As Integer, nBetaDepth As Integer, vlAlpha As Integer, vlBeta As Integer +Dim nStatus As Long, szMoveStr As String + +nTotal = CchessGenMoves(pos, mvs(0)) +nLegal = 0 +' 首先检查空着裁剪 +If CchessTryNull(pos) <> 0 Then + Infos(nLegal).szFen = MkBStr(CchessBoard2Fen(pos)) + CchessUndoNull pos + ProbeHash Infos(nLegal).mvBest, nAlphaDepth, nBetaDepth, vlAlpha, vlBeta, Infos(nLegal).szFen + Infos(nLegal).bExp = False + ' Infos(nLegal) 是可以用的,如果 nLegal ++ 不执行,那么它将被以后的结点覆盖 + If nBetaDepth > 0 Then + Infos(nLegal).nIcon = NODE_BETA + Infos(nLegal).vl = VALUE_BETA - vlBeta + Infos(nLegal).szInfo = "空着裁剪 " + Int2Str(nBetaDepth) + ":≥" + Int2Str(-vlBeta) + nLegal = nLegal + 1 + End If +End If +For i = 0 To nTotal - 1 + If CchessTryMove(pos, nStatus, mvs(i)) <> 0 Then + Infos(nLegal).szFen = MkBStr(CchessBoard2Fen(pos)) + Infos(nLegal).mvBest = 0 + CchessUndoMove pos + szMoveStr = MkC(CchessFile2Chin(CchessMove2File(mvs(i), pos), pos.sdPlayer)) + ProbeHash Infos(nLegal).mvBest, nAlphaDepth, nBetaDepth, vlAlpha, vlBeta, Infos(nLegal).szFen + Infos(nLegal).bExp = False + If nAlphaDepth > nBetaDepth Then + Infos(nLegal).nIcon = NODE_ALPHA + Infos(nLegal).vl = VALUE_ALPHA - vlAlpha + Infos(nLegal).szInfo = szMoveStr + " " + Int2Str(nAlphaDepth) + ":≤" + Int2Str(-vlAlpha) + ElseIf nBetaDepth > nAlphaDepth Then + Infos(nLegal).nIcon = NODE_BETA + Infos(nLegal).vl = VALUE_BETA - vlBeta + Infos(nLegal).szInfo = szMoveStr + " " + Int2Str(nBetaDepth) + ":≥" + Int2Str(-vlBeta) + Else + If nAlphaDepth = -1 Then + Infos(nLegal).bExp = True + Infos(nLegal).nIcon = NODE_NONE + Infos(nLegal).vl = VALUE_NONE + Infos(nLegal).szInfo = szMoveStr + Else + Infos(nLegal).bExp = (nAlphaDepth = 0) + Infos(nLegal).nIcon = IIf(nAlphaDepth = 0, NODE_LEAF, NODE_PV) + If vlAlpha < vlBeta Then + Infos(nLegal).szInfo = szMoveStr + " " + Int2Str(nAlphaDepth) + ":[" + Int2Str(-vlBeta) + "," + Int2Str(-vlAlpha) + "]" + ' 不是彻底的PV结点 + Infos(nLegal).vl = IIf(nAlphaDepth = 0, VALUE_LEAF_1, VALUE_PV_1) - vlAlpha + Else + Infos(nLegal).szInfo = szMoveStr + " " + Int2Str(nAlphaDepth) + ":=" + Int2Str(-vlAlpha) + Infos(nLegal).vl = IIf(nAlphaDepth = 0, VALUE_LEAF_2, VALUE_PV_2) - vlAlpha + End If + End If + End If + ' 最佳着法放在第一位 + If mvs(i) = mvBest Then + Infos(nLegal).vl = VALUE_BEST + End If + nLegal = nLegal + 1 + End If +Next +' 对结点做冒泡排序 +For i = nLegal - 1 To 1 Step -1 + For j = 0 To i - 1 + If Infos(j).vl < Infos(j + 1).vl Then + InfoTemp = Infos(j) + Infos(j) = Infos(j + 1) + Infos(j + 1) = InfoTemp + End If + Next +Next +' 输出结点 +For i = 0 To nLegal - 1 + If App_nNodeNum < MAX_NODE_NUM Then + App_Nodes(App_nNodeNum).szFen = Infos(i).szFen + App_Nodes(App_nNodeNum).mvBest = Infos(i).mvBest + App_Nodes(App_nNodeNum).bExp = Infos(i).bExp + App_nNodeNum = App_nNodeNum + 1 + If nIndex < 0 Then + frmUcciTree.tree.Nodes.Add , , , Infos(i).szInfo, Infos(i).nIcon + Else + frmUcciTree.tree.Nodes.Add nIndex + 1, tvwChild, , Infos(i).szInfo, Infos(i).nIcon + End If + End If +Next + +End Sub + +Public Sub DrawRoot() + +Dim i As Integer, j As Integer, pc As Integer, nPicId As Integer + +For i = 3 To 12 + For j = 3 To 11 + pc = App_posRoot.ucpcSquares(i * 16 + j) + If pc = 0 Then + nPicId = 0 + Else + nPicId = PieceType(pc) + IIf(pc < 32, 1, 8) + End If + frmPosition.imgSquares((i - 3) * 9 + j - 3).Picture = frmUcciTree.imgPiecesMini(nPicId).Picture + Next +Next + +End Sub + +Public Sub DrawCurr(ByRef pos As PositionStruct) + +Dim i As Integer, j As Integer, pc As Integer, nPicId As Integer +For i = 3 To 12 + For j = 3 To 11 + pc = pos.ucpcSquares(i * 16 + j) + If pc = 0 Then + nPicId = 0 + Else + nPicId = PieceType(pc) + IIf(pc < 32, 1, 8) + End If + frmPosition.imgSquares2((i - 3) * 9 + j - 3).Picture = frmUcciTree.imgPiecesMini(nPicId).Picture + Next +Next + +End Sub + +Public Sub Main() + +Dim lpStr As Long, szRecv As String, szEngine As String + +szEngine = OpenFileDialog("加载引擎", "引擎文件 (*.EXE)|*.EXE|所有文件 (*.*)|*.*") +If szEngine <> "" Then + CchessInit + CchessFen2Board App_posRoot, CCHESS_START_FEN + DrawRoot + App_szCurrFen = CCHESS_START_FEN + DrawCurr App_posRoot + PipeOpen App_pipe, szEngine + PipeOut "ucci" + PipeOut "setoption promotion false" + PipeOut "setoption usebook false" + frmPosition.Show , frmUcciTree + frmUcciTree.Show + frmUcciTree.cmbSend.List(0) = "position fen " + CCHESS_START_FEN + " - - 0 1" + App_nNodeNum = 0 + App_bRunning = True + Do While App_bRunning + Sleep 1 + DoEvents + szRecv = PipeIn + If szRecv <> "" Then + frmUcciTree.stb.Panels("Info").Text = szRecv + If Left(szRecv, 9) = "bestmove " Then + AddNode App_posRoot, Coord2Move(Mid(szRecv, 10, 4)) + End If + End If + Loop + PipeOut "quit" + PipeClose App_pipe +End If + +End Sub diff --git a/TREE/UCCITREE.FRM b/TREE/UCCITREE.FRM new file mode 100644 index 0000000..0a5229a --- /dev/null +++ b/TREE/UCCITREE.FRM @@ -0,0 +1,363 @@ +VERSION 5.00 +Object = "{831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0#0"; "MSCOMCTL.OCX" +Begin VB.Form frmUcciTree + Caption = "搜索树分析器" + ClientHeight = 3195 + ClientLeft = 60 + ClientTop = 345 + ClientWidth = 4680 + BeginProperty Font + Name = "宋体" + Size = 9 + Charset = 0 + Weight = 400 + Underline = 0 'False + Italic = 0 'False + Strikethrough = 0 'False + EndProperty + ScaleHeight = 3195 + ScaleWidth = 4680 + StartUpPosition = 3 'Windows Default + Begin VB.ComboBox cmbSend + Height = 300 + ItemData = "UCCITREE.frx":0000 + Left = 0 + List = "UCCITREE.frx":000D + TabIndex = 0 + Top = 120 + Width = 2775 + End + Begin VB.CommandButton btnExit + Cancel = -1 'True + Caption = "退出" + Height = 375 + Left = 3840 + TabIndex = 2 + Top = 120 + Width = 735 + End + Begin VB.CommandButton btnSend + Caption = "发送" + Height = 375 + Left = 3000 + TabIndex = 1 + Top = 120 + Width = 735 + End + Begin MSComctlLib.StatusBar stb + Align = 2 'Align Bottom + Height = 255 + Left = 0 + TabIndex = 4 + Top = 2940 + Width = 4680 + _ExtentX = 8255 + _ExtentY = 450 + _Version = 393216 + BeginProperty Panels {8E3867A5-8586-11D1-B16A-00C0F0283628} + NumPanels = 1 + BeginProperty Panel1 {8E3867AB-8586-11D1-B16A-00C0F0283628} + Object.Width = 16933 + MinWidth = 16933 + Key = "Info" + EndProperty + EndProperty + End + Begin MSComctlLib.TreeView tree + Height = 2295 + Left = 0 + TabIndex = 3 + Top = 600 + Width = 4575 + _ExtentX = 8070 + _ExtentY = 4048 + _Version = 393217 + Indentation = 212 + LabelEdit = 1 + LineStyle = 1 + Style = 7 + ImageList = "imglst" + Appearance = 1 + BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851} + Name = "宋体" + Size = 9 + Charset = 134 + Weight = 400 + Underline = 0 'False + Italic = 0 'False + Strikethrough = 0 'False + EndProperty + End + Begin MSComctlLib.ImageList imglst + Left = 0 + Top = 0 + _ExtentX = 1005 + _ExtentY = 1005 + BackColor = -2147483643 + ImageWidth = 16 + ImageHeight = 16 + MaskColor = 12632256 + _Version = 393216 + BeginProperty Images {2C247F25-8591-11D1-B16A-00C0F0283628} + NumListImages = 5 + BeginProperty ListImage1 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "UCCITREE.frx":0044 + Key = "" + EndProperty + BeginProperty ListImage2 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "UCCITREE.frx":019E + Key = "" + EndProperty + BeginProperty ListImage3 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "UCCITREE.frx":02F8 + Key = "" + EndProperty + BeginProperty ListImage4 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "UCCITREE.frx":0452 + Key = "" + EndProperty + BeginProperty ListImage5 {2C247F27-8591-11D1-B16A-00C0F0283628} + Picture = "UCCITREE.frx":05AC + Key = "" + EndProperty + EndProperty + End + Begin VB.Image imgPiecesMini + Height = 240 + Index = 0 + Left = 0 + Picture = "UCCITREE.frx":0706 + Top = 600 + Width = 240 + End + Begin VB.Image imgPiecesMini + Height = 240 + Index = 1 + Left = 240 + Picture = "UCCITREE.frx":0A52 + Top = 600 + Width = 240 + End + Begin VB.Image imgPiecesMini + Height = 240 + Index = 2 + Left = 480 + Picture = "UCCITREE.frx":0DD8 + Top = 600 + Width = 240 + End + Begin VB.Image imgPiecesMini + Height = 240 + Index = 3 + Left = 720 + Picture = "UCCITREE.frx":1156 + Top = 600 + Width = 240 + End + Begin VB.Image imgPiecesMini + Height = 240 + Index = 4 + Left = 960 + Picture = "UCCITREE.frx":14D9 + Top = 600 + Width = 240 + End + Begin VB.Image imgPiecesMini + Height = 240 + Index = 5 + Left = 1200 + Picture = "UCCITREE.frx":185B + Top = 600 + Width = 240 + End + Begin VB.Image imgPiecesMini + Height = 240 + Index = 6 + Left = 1440 + Picture = "UCCITREE.frx":1BD8 + Top = 600 + Width = 240 + End + Begin VB.Image imgPiecesMini + Height = 240 + Index = 7 + Left = 1680 + Picture = "UCCITREE.frx":1F5E + Top = 600 + Width = 240 + End + Begin VB.Image imgPiecesMini + Height = 240 + Index = 8 + Left = 1920 + Picture = "UCCITREE.frx":22DC + Top = 600 + Width = 240 + End + Begin VB.Image imgPiecesMini + Height = 240 + Index = 9 + Left = 2160 + Picture = "UCCITREE.frx":2660 + Top = 600 + Width = 240 + End + Begin VB.Image imgPiecesMini + Height = 240 + Index = 10 + Left = 2400 + Picture = "UCCITREE.frx":29D6 + Top = 600 + Width = 240 + End + Begin VB.Image imgPiecesMini + Height = 240 + Index = 11 + Left = 2640 + Picture = "UCCITREE.frx":2D5B + Top = 600 + Width = 240 + End + Begin VB.Image imgPiecesMini + Height = 240 + Index = 12 + Left = 2880 + Picture = "UCCITREE.frx":30DD + Top = 600 + Width = 240 + End + Begin VB.Image imgPiecesMini + Height = 240 + Index = 13 + Left = 3120 + Picture = "UCCITREE.frx":345A + Top = 600 + Width = 240 + End + Begin VB.Image imgPiecesMini + Height = 240 + Index = 14 + Left = 3360 + Picture = "UCCITREE.frx":37E0 + Top = 600 + Width = 240 + End +End +Attribute VB_Name = "frmUcciTree" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False +' Search Tree Analyst - for UCCI Engines +' Designed by Morning Yellow, Version: 3.01, Last Modified: Nov. 2007 +' Copyright (C) 2004-2007 www.elephantbase.net +' +' This library is free software; you can redistribute it and/or +' modify it under the terms of the GNU Lesser General Public +' License as published by the Free Software Foundation; either +' version 2.1 of the License, or (at your option) any later version. +' +' This library 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 +' Lesser General Public License for more details. +' +' You should have received a copy of the GNU Lesser General Public +' License along with this library; if not, write to the Free Software +' Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Option Explicit + +Private Sub Form_UnLoad(nCancel As Integer) + +App_bRunning = False + +End Sub + +Private Sub Form_Resize() + +If WindowState = 1 Then + Exit Sub +End If +If Height < 3600 Then + Height = 3600 +End If +If Width < 4800 Then + Width = 4800 +End If +cmbSend.Width = Width - 1920 +btnSend.Left = Width - 1800 +btnExit.Left = Width - 960 +tree.Height = Height - 1320 +tree.Width = Width - 120 + +End Sub + +Private Sub btnSend_Click() + +Dim sz As String, i As Integer, j As Integer +Dim mv As Long, nStatus As Long +If cmbSend.Text <> vbNullString Then + sz = cmbSend.Text + If UCase(Left(sz, 4)) = "QUIT" Then + MsgBox "请按“退出”按钮!", vbExclamation + Exit Sub + End If + PipeOut sz + If Left(sz, 9) = "position " Then + i = InStr(sz, " moves ") + If Mid(sz, 10, 4) = "fen " Then + If i > 0 Then + CchessFen2Board App_posRoot, Mid(sz, 14, i - 14) + Else + CchessFen2Board App_posRoot, Mid(sz, 14) + End If + ElseIf Mid(sz, 10, 8) = "startpos" Then + CchessFen2Board App_posRoot, CCHESS_START_FEN + End If + If i > 0 Then + i = i + 7 + Do While i < Len(sz) + mv = Coord2Move(Mid(sz, i)) + CchessTryMove App_posRoot, nStatus, mv + i = i + 5 + Loop + End If + DrawRoot + ElseIf Left(sz, 3) = "go " Then + tree.Nodes.Clear + App_nNodeNum = 0 + ElseIf Left(sz, 20) = "setoption promotion " Then + If Mid(sz, 21) = "on" Or Mid(sz, 21) = "true" Then + CchessPromotion 1 + Else + CchessPromotion 0 + End If + End If +End If +cmbSend.Text = "" + +End Sub + +Private Sub btnExit_Click() + +Unload Me + +End Sub + +Private Sub tree_NodeClick(ByVal n As Node) + +Dim pos As PositionStruct +Dim nIndex As Integer + +nIndex = n.Index - 1 +cmbSend.List(0) = "position fen " + App_Nodes(nIndex).szFen + " - - 0 1" +CchessFen2Board pos, App_Nodes(nIndex).szFen +App_szCurrFen = App_Nodes(nIndex).szFen +DrawCurr pos +If Not App_Nodes(nIndex).bExp Then + App_Nodes(nIndex).bExp = True + AddNode pos, App_Nodes(nIndex).mvBest, nIndex +End If + +End Sub diff --git a/TREE/UCCITREE.FRX b/TREE/UCCITREE.FRX new file mode 100644 index 0000000..b7338cb Binary files /dev/null and b/TREE/UCCITREE.FRX differ diff --git a/TREE/UCCITREE.VBP b/TREE/UCCITREE.VBP new file mode 100644 index 0000000..7229fc6 --- /dev/null +++ b/TREE/UCCITREE.VBP @@ -0,0 +1,42 @@ +Type=Exe +Form=UCCITREE.FRM +Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\WINDOWS\system32\stdole2.tlb#OLE Automation +Object={831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0#0; MSCOMCTL.OCX +Module=mdlCChess; ..\cchess\CCHESS.BAS +Module=mdlPipe; ..\utility\PIPE.BAS +Module=mdlUcciTree; UCCITREE.BAS +Form=POSITION.FRM +Module=mdlBase; ..\utility\BASE.BAS +IconForm="frmUcciTree" +Startup="Sub Main" +HelpFile="" +ExeName32="UCCITREE.EXE" +Path32="..\BIN" +Command32="" +Name="prjUcciTree" +HelpContextID="0" +CompatibleMode="0" +MajorVer=1 +MinorVer=0 +RevisionVer=0 +AutoIncrementVer=0 +ServerSupportFiles=0 +VersionCompanyName="Unknown Organization" +CompilationType=0 +OptimizationType=0 +FavorPentiumPro(tm)=0 +CodeViewDebugInfo=0 +NoAliasing=0 +BoundsCheck=0 +OverflowCheck=0 +FlPointCheck=0 +FDIVCheck=0 +UnroundedFP=0 +StartMode=0 +Unattended=0 +Retained=0 +ThreadPerObject=0 +MaxNumberOfThreads=1 + +[MS Transaction Server] +AutoRefresh=1 diff --git a/UCCI2QH/ELEEYE.ISS b/UCCI2QH/ELEEYE.ISS new file mode 100644 index 0000000..405d3fc --- /dev/null +++ b/UCCI2QH/ELEEYE.ISS @@ -0,0 +1,47 @@ +; Script generated by the Inno Setup Script Wizard. +; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! + +[Languages] +Name: "zh"; MessagesFile: "compiler:Languages\ChineseSimp.isl"; InfoBeforeFile: "..\LGPL.TXT" + +[Setup] +AppName=ElephantEye +AppVerName=ElephantEye 3.1 (《象棋巫师》和《浅红象棋》引擎) +AppPublisher=www.elephantbase.net +AppPublisherURL=http://www.elephantbase.net/ +AppSupportURL=http://www.elephantbase.net/ +AppUpdatesURL=http://www.elephantbase.net/ +DefaultDirName={pf}\Qianhong\Plugins\ElephantEye +DefaultGroupName=象棋巫师 +OutputBaseFilename=eleeye +OutputDir=. +Compression=lzma +SolidCompression=yes +Uninstallable=no +SetupIconFile=..\RES\XQWSETUP.ICO +AppCopyright=(C) 2004-2007 www.elephantbase.net + +[Files] +Source: "UCCI2QH.INI"; DestDir: "{app}"; Flags: ignoreversion +Source: "..\ELEBASE.URL"; DestDir: "{app}"; Flags: ignoreversion +Source: "..\README.TXT"; DestDir: "{app}"; Flags: ignoreversion +Source: "..\LGPL.TXT"; DestDir: "{app}"; Flags: ignoreversion +Source: "..\BIN\UCCI2QH.EXE"; DestDir: "{app}"; Flags: ignoreversion +Source: "..\BIN\ELEEYE.EXE"; DestDir: "{app}\BIN"; Flags: ignoreversion +Source: "..\BIN\EVALUATE.DLL"; DestDir: "{app}\BIN"; Flags: ignoreversion +Source: "..\BIN\BOOK.DAT"; DestDir: "{app}\BIN"; Flags: ignoreversion +Source: "..\BIN\MAKEBOOK.EXE"; DestDir: "{app}\BIN"; Flags: ignoreversion +Source: "..\BIN\MAKEBOOK.DLL"; DestDir: "{app}\BIN"; Flags: ignoreversion +Source: "..\BIN\CCHESS.DLL"; DestDir: "{app}\BIN"; Flags: ignoreversion + +[Registry] +Root: HKCU; Subkey: "Software\VB and VBA Program Settings\XQWizard\Engine"; ValueType: string; ValueName: "FileName"; ValueData: "{app}\BIN\ELEEYE.EXE" +Root: HKCU; Subkey: "Software\VB and VBA Program Settings\XQWizard\Engine"; ValueType: string; ValueName: "BookFiles"; ValueData: "{app}\BIN\BOOK.DAT" + +[Icons] +Name: "{group}\象棋巫师的网站"; Filename: "{app}\ELEBASE.URL" +Name: "{group}\ElephantEye 开局库管理工具"; Filename: "{app}\BIN\MAKEBOOK.EXE" +Name: "{group}\ElephantEye 使用说明"; Filename: "{app}\README.TXT" + +[Run] +Filename: "{app}\README.TXT"; Description: "阅读 ElephantEye 使用说明"; Flags: nowait postinstall shellexec skipifsilent diff --git a/UCCI2QH/MAKEFILE.BAT b/UCCI2QH/MAKEFILE.BAT new file mode 100644 index 0000000..0f20e3a --- /dev/null +++ b/UCCI2QH/MAKEFILE.BAT @@ -0,0 +1,5 @@ +@ECHO OFF +RC ..\RES\UCCI2QH.RC +CL /DNDEBUG /O2 /W3 /Fe..\BIN\UCCI2QH.EXE ..\BASE\PIPE.CPP ..\ELEEYE\PREGEN.CPP ..\ELEEYE\POSITION.CPP ..\ELEEYE\GENMOVES.CPP UCCI2QH.CPP ..\RES\UCCI2QH.RES +DEL ..\RES\UCCI2QH.RES +DEL *.OBJ diff --git a/UCCI2QH/UCCI2QH.INI b/UCCI2QH/UCCI2QH.INI new file mode 100644 index 0000000..70a20e9 --- /dev/null +++ b/UCCI2QH/UCCI2QH.INI @@ -0,0 +1,19 @@ +[UCCI2QH] +Name=ElephantEye 3.1 +File=BIN\ELEEYE.EXE +Info=ElephantEye 3.1 - AI Plugin for Qianhong +Info=Designed by Morning Yellow +Info=Copyright (C) 2004-2007 www.elephantbase.net +Info=Authorized to ElephantEye Test Team +Level=Beginner +Level=Amateur +Level=Expert +Level=Master +Level=Grand-Master +Level=Overwhelming +ThinkMode=time 60000 movestogo 999 +ThinkMode=time 60000 movestogo 216 +ThinkMode=time 60000 movestogo 36 +ThinkMode=time 60000 movestogo 6 +ThinkMode=time 60000 movestogo 1 +ThinkMode=infinite diff --git a/UCCI2QH/UCCI2QH.UML b/UCCI2QH/UCCI2QH.UML new file mode 100644 index 0000000..7551dd1 --- /dev/null +++ b/UCCI2QH/UCCI2QH.UML @@ -0,0 +1,887 @@ + + + + + + + + + + +UCCI2QH +1 + +UCCI2QH +DtjvsPSdW0W+Dhe2Ua8/NQAA +1 + +寮曟搸鐘舵 +0xcDqa0wHEy/sRPO9HWBwwAA + +TOP +nr9Lx8SpPkyyyW+S0Q2ciwAA +9 + +pkInitial +cC+ljC7DQUacOoT/sHo1WAAA +1 +8ZWAUYVjBkynSm1IeZkveQAA +1 +xGTHmpMWHEOm7R4Ccz0HsgAA + + +IDLE_NONE +cC+ljC7DQUacOoT/sHo1WAAA +2 +nzbsgp8fNESKPDPptSBLqgAA +/MqbyXLmjEmzxRywyp61tgAA +4 +K0Gq0NcZ/U+hcnaozMphOgAA +0+xxXbfMDk2HUlsB6h0LBgAA +l+hWK7WbyUqID/W9uQezPAAA +cJhcZYd7SU6LVwd1vh3MFAAA +5 +xGTHmpMWHEOm7R4Ccz0HsgAA +uOXHMV0ZZEWOne3vOtb7igAA +QBnuKJkT+UalcgHnXVkomAAA +P20bYGofKEeavTticcfVbQAA +RSws/+dOo0mpxIHVhXM+/AAA + + +BUSY_THINK +cC+ljC7DQUacOoT/sHo1WAAA +2 +OkZPI9ZhMEmywdS7hu4MxAAA +jMAG7ppcfkqUVa04hGjVRgAA +2 +P20bYGofKEeavTticcfVbQAA +LL4xH45CjUS1VljFC0cgzwAA +2 +K0Gq0NcZ/U+hcnaozMphOgAA +ZK+ScEVw10e/JasQ9KHirwAA + + +BUSY_HINT +cC+ljC7DQUacOoT/sHo1WAAA +2 +crxBedAlokGpo21PjPehxwAA +Esvr5ha5TEKfOk1C8vM1RQAA +1 +QBnuKJkT+UalcgHnXVkomAAA +1 +0+xxXbfMDk2HUlsB6h0LBgAA + + +BUSY_PONDER +cC+ljC7DQUacOoT/sHo1WAAA +2 +uGPa5PkttEmM0OtFSrK1fAAA +mvgYm6PSBEO8D2DZtxStTQAA +3 +uOXHMV0ZZEWOne3vOtb7igAA +P/IfTOCHG02NxIegm4xqgAAA +iHbFoqpWikeyxsV5b2UQlAAA +3 +l+hWK7WbyUqID/W9uQezPAAA +LL4xH45CjUS1VljFC0cgzwAA +VYQs4SwxP0C4AUV5em6oIwAA + + +BUSY_PONDERHIT +cC+ljC7DQUacOoT/sHo1WAAA +2 +3CWvAqFxy0i9yAqQtQGxvwAA +C8tdiGzIL0ejFnZTM426+QAA +2 +ZK+ScEVw10e/JasQ9KHirwAA +ZFoVxYv44kmCF/Zj0e4RuQAA +1 +P/IfTOCHG02NxIegm4xqgAAA + + +IDLE_PONDER_FINISHED +cC+ljC7DQUacOoT/sHo1WAAA +2 +faqAE4SHDU2RQ3zPZ18eBAAA +Oz6pHtIfJkG2yJuV9xiTdAAA +2 +3TMZMnEsLEiqjyFq5mC2rQAA +RSws/+dOo0mpxIHVhXM+/AAA +1 +iHbFoqpWikeyxsV5b2UQlAAA + + +IDLE_PONDERHIT_FINISHED +cC+ljC7DQUacOoT/sHo1WAAA +2 +vuG7sn7zMkuy2l8jYFNVNwAA +sPjlY405QEKu7tt79z5t1wAA +1 +VYQs4SwxP0C4AUV5em6oIwAA +2 +ZFoVxYv44kmCF/Zj0e4RuQAA +3TMZMnEsLEiqjyFq5mC2rQAA + + +cC+ljC7DQUacOoT/sHo1WAAA +1 +Nvplq46bPkCGlawW0Z8WlAAA +1 +cJhcZYd7SU6LVwd1vh3MFAAA + + +1 + +寮曟搸鐘舵 +nr9Lx8SpPkyyyW+S0Q2ciwAA + +vpq3DbwYtU+toNMG/Zp8rAAA +24 + +clMaroon +$00B9FFFF +80 +168 +20 +20 +YxP6VYFkPEOJ8IUVzqydIwAA + + +clMaroon +$00B9FFFF +180 +300 +76 +40 +TTOcitf68U2bRBKTQRVGFwAA + + +IDLE_NONE + + +False + + +False + + + +TTOcitf68U2bRBKTQRVGFwAA + + + +clMaroon +$00B9FFFF +316 +136 +71 +40 +mbHINkWLDE+1qVAHxNyktQAA + + +BUSY_THINK + + +False + + +False + + + +mbHINkWLDE+1qVAHxNyktQAA + + + +clMaroon +$00B9FFFF +233,300;335,175 +K0Gq0NcZ/U+hcnaozMphOgAA +OkZPI9ZhMEmywdS7hu4MxAAA +nzbsgp8fNESKPDPptSBLqgAA + +-3.97144636394559 +19.0262975904404 +AI +K0Gq0NcZ/U+hcnaozMphOgAA + + +False +1.5707963267949 +30 +K0Gq0NcZ/U+hcnaozMphOgAA + + +False +-1.5707963267949 +15 +K0Gq0NcZ/U+hcnaozMphOgAA + + + +clMaroon +$00B9FFFF +12 +300 +80 +40 +/xbZlDymaEGgMflCOEZ7PgAA + + +BUSY_HINT + + +False + + +False + + + +/xbZlDymaEGgMflCOEZ7PgAA + + + +clMaroon +$00B9FFFF +180,330;136,344;91,331 +0+xxXbfMDk2HUlsB6h0LBgAA +crxBedAlokGpo21PjPehxwAA +nzbsgp8fNESKPDPptSBLqgAA + +1.45787754474148 +11.1803398874989 +HINT +0+xxXbfMDk2HUlsB6h0LBgAA + + +False +1.5707963267949 +30 +0+xxXbfMDk2HUlsB6h0LBgAA + + +False +-1.5707963267949 +15 +0+xxXbfMDk2HUlsB6h0LBgAA + + + +clMaroon +$00B9FFFF +340 +300 +82 +40 +CN3fmVvi+0e8GJKii+qZKwAA + + +BUSY_PONDER + + +False + + +False + + + +CN3fmVvi+0e8GJKii+qZKwAA + + + +clMaroon +$00B9FFFF +98,187;200,300 +xGTHmpMWHEOm7R4Ccz0HsgAA +nzbsgp8fNESKPDPptSBLqgAA +8ZWAUYVjBkynSm1IeZkveQAA + +-2.6853002264206 +45.3100430368368 +寮曟搸鍚姩 +xGTHmpMWHEOm7R4Ccz0HsgAA + + +False +1.5707963267949 +30 +xGTHmpMWHEOm7R4Ccz0HsgAA + + +False +-1.5707963267949 +15 +xGTHmpMWHEOm7R4Ccz0HsgAA + + + +clMaroon +$00B9FFFF +340,319;255,319 +uOXHMV0ZZEWOne3vOtb7igAA +nzbsgp8fNESKPDPptSBLqgAA +uGPa5PkttEmM0OtFSrK1fAAA + +4.53771555731692 +17.2626765016321 +PLAY(鏈懡涓) +uOXHMV0ZZEWOne3vOtb7igAA + + +False +1.5707963267949 +30 +uOXHMV0ZZEWOne3vOtb7igAA + + +False +-1.5707963267949 +15 +uOXHMV0ZZEWOne3vOtb7igAA + + + +clMaroon +$00B9FFFF +464 +224 +99 +40 +LvzBMN4eKU+gVOH4KN+LrgAA + + +BUSY_PONDERHIT + + +False + + +False + + + +LvzBMN4eKU+gVOH4KN+LrgAA + + + +clMaroon +$00B9FFFF +413,300;478,263 +P/IfTOCHG02NxIegm4xqgAAA +3CWvAqFxy0i9yAqQtQGxvwAA +uGPa5PkttEmM0OtFSrK1fAAA + +-4.239969262392 +17.8044938147649 +PLAY(鍛戒腑) +P/IfTOCHG02NxIegm4xqgAAA + + +False +1.5707963267949 +30 +P/IfTOCHG02NxIegm4xqgAAA + + +False +-1.5707963267949 +15 +P/IfTOCHG02NxIegm4xqgAAA + + + +clMaroon +$00B9FFFF +292 +432 +132 +40 +RUI9oBmwG0GrfVWstzOqIgAA + + +IDLE_PONDER_FINISHED + + +False + + +False + + + +RUI9oBmwG0GrfVWstzOqIgAA + + + +clMaroon +$00B9FFFF +478,224;386,174 +ZK+ScEVw10e/JasQ9KHirwAA +OkZPI9ZhMEmywdS7hu4MxAAA +3CWvAqFxy0i9yAqQtQGxvwAA + +4.61807444092792 +12.369316876853 +AI +ZK+ScEVw10e/JasQ9KHirwAA + + +False +1.5707963267949 +30 +ZK+ScEVw10e/JasQ9KHirwAA + + +False +-1.5707963267949 +15 +ZK+ScEVw10e/JasQ9KHirwAA + + + +clMaroon +$00B9FFFF +91,304;136,288;180,305 +QBnuKJkT+UalcgHnXVkomAAA +nzbsgp8fNESKPDPptSBLqgAA +crxBedAlokGpo21PjPehxwAA + +-3.59906748658937 +13.4536240470737 +鎬濊冪粨鏉 +QBnuKJkT+UalcgHnXVkomAAA + + +False +1.5707963267949 +30 +QBnuKJkT+UalcgHnXVkomAAA + + +False +-1.5707963267949 +15 +QBnuKJkT+UalcgHnXVkomAAA + + + +clMaroon +$00B9FFFF +316,171;236,208;220,300 +P20bYGofKEeavTticcfVbQAA +nzbsgp8fNESKPDPptSBLqgAA +OkZPI9ZhMEmywdS7hu4MxAAA + +-1.98660839226433 +32.4499614791759 +鎬濊冪粨鏉(鏃犲悗鍙版濊) +P20bYGofKEeavTticcfVbQAA + + +False +1.5707963267949 +30 +P20bYGofKEeavTticcfVbQAA + + +False +-1.5707963267949 +15 +P20bYGofKEeavTticcfVbQAA + + + +clMaroon +$00B9FFFF +377,339;360,432 +iHbFoqpWikeyxsV5b2UQlAAA +faqAE4SHDU2RQ3zPZ18eBAAA +uGPa5PkttEmM0OtFSrK1fAAA + +-1.87954511704732 +32.649655434629 +鎬濊冪粨鏉 +iHbFoqpWikeyxsV5b2UQlAAA + + +False +1.5707963267949 +30 +iHbFoqpWikeyxsV5b2UQlAAA + + +False +-1.5707963267949 +15 +iHbFoqpWikeyxsV5b2UQlAAA + + + +clMaroon +$00B9FFFF +440 +356 +149 +40 +MpCQbaao/0mOmCLX0+jWNgAA + + +IDLE_PONDERHIT_FINISHED + + +False + + +False + + + +MpCQbaao/0mOmCLX0+jWNgAA + + + +clMaroon +$00B9FFFF +513,263;514,356 +ZFoVxYv44kmCF/Zj0e4RuQAA +vuG7sn7zMkuy2l8jYFNVNwAA +3CWvAqFxy0i9yAqQtQGxvwAA + +1.5657015401253 +28.0178514522438 +鎬濊冪粨鏉 +ZFoVxYv44kmCF/Zj0e4RuQAA + + +False +1.5707963267949 +30 +ZFoVxYv44kmCF/Zj0e4RuQAA + + +False +-1.5707963267949 +15 +ZFoVxYv44kmCF/Zj0e4RuQAA + + + +clMaroon +$00B9FFFF +396,432;473,395 +3TMZMnEsLEiqjyFq5mC2rQAA +vuG7sn7zMkuy2l8jYFNVNwAA +faqAE4SHDU2RQ3zPZ18eBAAA + +-0.536645807489513 +35.2278299076171 +PLAY(鍛戒腑) +3TMZMnEsLEiqjyFq5mC2rQAA + + +False +1.5707963267949 +30 +3TMZMnEsLEiqjyFq5mC2rQAA + + +False +-1.5707963267949 +15 +3TMZMnEsLEiqjyFq5mC2rQAA + + + +clMaroon +$00B9FFFF +337,432;238,339 +RSws/+dOo0mpxIHVhXM+/AAA +nzbsgp8fNESKPDPptSBLqgAA +faqAE4SHDU2RQ3zPZ18eBAAA + +1.16273775930712 +32.6955654485436 +PLAY(鏈懡涓) +RSws/+dOo0mpxIHVhXM+/AAA + + +False +1.5707963267949 +30 +RSws/+dOo0mpxIHVhXM+/AAA + + +False +-1.5707963267949 +15 +RSws/+dOo0mpxIHVhXM+/AAA + + + +clMaroon +$00B9FFFF +469,356;421,336 +VYQs4SwxP0C4AUV5em6oIwAA +uGPa5PkttEmM0OtFSrK1fAAA +vuG7sn7zMkuy2l8jYFNVNwAA + +4.83301140261525 +10.816653826392 +AI +VYQs4SwxP0C4AUV5em6oIwAA + + +False +1.5707963267949 +30 +VYQs4SwxP0C4AUV5em6oIwAA + + +False +-1.5707963267949 +15 +VYQs4SwxP0C4AUV5em6oIwAA + + + +clMaroon +$00B9FFFF +355,175;377,300 +LL4xH45CjUS1VljFC0cgzwAA +uGPa5PkttEmM0OtFSrK1fAAA +OkZPI9ZhMEmywdS7hu4MxAAA + +1.30727576073992 +11.0453610171873 +鎬濊冪粨鏉(鏈夊悗鍙版濊) +LL4xH45CjUS1VljFC0cgzwAA + + +False +1.5707963267949 +30 +LL4xH45CjUS1VljFC0cgzwAA + + +False +-1.5707963267949 +15 +LL4xH45CjUS1VljFC0cgzwAA + + + +clMaroon +$00B9FFFF +76 +432 +26 +26 +TnN1/tXaskCM+jnEMClKkgAA + + +clMaroon +$00B9FFFF +196,339;100,432 +cJhcZYd7SU6LVwd1vh3MFAAA +Nvplq46bPkCGlawW0Z8WlAAA +nzbsgp8fNESKPDPptSBLqgAA + +-0.515540545524967 +50.6951674225463 +寮曟搸閫鍑 +cJhcZYd7SU6LVwd1vh3MFAAA + + +False +1.5707963267949 +30 +cJhcZYd7SU6LVwd1vh3MFAAA + + +False +-1.5707963267949 +15 +cJhcZYd7SU6LVwd1vh3MFAAA + + + + +16 + +AI +nr9Lx8SpPkyyyW+S0Q2ciwAA +TTOcitf68U2bRBKTQRVGFwAA +mbHINkWLDE+1qVAHxNyktQAA +4 +R7PGWctK3UeW7gUjx8h6RwAA +ZKSLYKZeTEy6voEZPtYY1gAA +5bXKQvcTZ0CjDwfSkuOZewAA +EScgKWuH8Uurx5le1aGZDQAA + + +HINT +nr9Lx8SpPkyyyW+S0Q2ciwAA +TTOcitf68U2bRBKTQRVGFwAA +/xbZlDymaEGgMflCOEZ7PgAA +4 +3Bl1WUyUoE6Td4GRmBXPngAA +OVo5Q+k8yUWEnFGeKq/YBgAA +Rz4WBWJJO0G4nyYotZu/fwAA +J72npUMoWEOHg5SOqt99qwAA + + +PLAY +nr9Lx8SpPkyyyW+S0Q2ciwAA +TTOcitf68U2bRBKTQRVGFwAA +CN3fmVvi+0e8GJKii+qZKwAA + + +寮曟搸鍚姩 +nr9Lx8SpPkyyyW+S0Q2ciwAA +YxP6VYFkPEOJ8IUVzqydIwAA +TTOcitf68U2bRBKTQRVGFwAA +4 +cGbksWHvqkWg5/2foqVUIgAA +75DRnrHqhUOXmCHTKnlSJgAA +Cv8tJ4NiSEqH0fORKC88cQAA +xSBeTTL1ZUaUh24G4xK7BAAA + + +PLAY(鏈懡涓) +nr9Lx8SpPkyyyW+S0Q2ciwAA +CN3fmVvi+0e8GJKii+qZKwAA +TTOcitf68U2bRBKTQRVGFwAA +4 +pa1EDHWTrkmEjouLPhpCwgAA +7O2KCED8BUypv5W0PvdpvwAA +TQaIVTLWDk2xFpwoZOlWegAA +nXjbtf4X5UCnTmP9c/Zk3wAA + + +PLAY(鍛戒腑) +nr9Lx8SpPkyyyW+S0Q2ciwAA +CN3fmVvi+0e8GJKii+qZKwAA +LvzBMN4eKU+gVOH4KN+LrgAA +4 +fVgIcqPvrEiAErzOwSIKTAAA +If6pD1JSo0yWFDlwsVHBAgAA +LpazWyHfpE+7MnDzIPMaigAA +XQ4BXB3V3kaMuMzZ30wb5AAA + + +AI +nr9Lx8SpPkyyyW+S0Q2ciwAA +LvzBMN4eKU+gVOH4KN+LrgAA +mbHINkWLDE+1qVAHxNyktQAA +4 +MUqiLTYbTE+h5c6/qESuHwAA +qOUEc4S+sEGNexxLqoenXwAA +9Bq8lVExsESCH9rrwFOhlQAA +qH5evDSdYUadyoDiNgUpNgAA + + +鎬濊冪粨鏉 +nr9Lx8SpPkyyyW+S0Q2ciwAA +/xbZlDymaEGgMflCOEZ7PgAA +TTOcitf68U2bRBKTQRVGFwAA +4 +ujrxoDnuV0mTgaJyEsx4ygAA ++Ez1TJ+nbUq+PaPu65ca/QAA +gpuCaFGigkuzci3RXgqtPQAA +WNSbqWhx3EqCd9trCVMPOwAA + + +鎬濊冪粨鏉(鏃犲悗鍙版濊) +nr9Lx8SpPkyyyW+S0Q2ciwAA +mbHINkWLDE+1qVAHxNyktQAA +TTOcitf68U2bRBKTQRVGFwAA +4 +d7FTt5ST4kGYBdAZcWeCzAAA +Xxc659Wrc0G07o6YGM/akgAA +FLPpNpmVr0qVg3PtjJ4A8wAA +wLENHNfqNUWLiY21lzmeCAAA + + +鎬濊冪粨鏉 +nr9Lx8SpPkyyyW+S0Q2ciwAA +CN3fmVvi+0e8GJKii+qZKwAA +RUI9oBmwG0GrfVWstzOqIgAA +4 +5dxhkJCV8kGHrL9eB2QDnAAA +vv0diWY3X0i8Mjl6pRSCdQAA +IRfOdoXOVEeOodP3Tozl6QAA +eyTivVyd/0K2AOgRnzmQQgAA + + +鎬濊冪粨鏉 +nr9Lx8SpPkyyyW+S0Q2ciwAA +LvzBMN4eKU+gVOH4KN+LrgAA +MpCQbaao/0mOmCLX0+jWNgAA +4 +f7KhNEKNc0Cdm0HiCmr9hgAA +5dQNDL7WB0SJ2XvbTq5FVQAA +TczlZClevk6BYQ+kPLfNRwAA +Iwms3rOkoEu9vtq+x+zZwQAA + + +PLAY(鍛戒腑) +nr9Lx8SpPkyyyW+S0Q2ciwAA +RUI9oBmwG0GrfVWstzOqIgAA +MpCQbaao/0mOmCLX0+jWNgAA +4 +XPNrck9+uEKddnhkUaxBBwAA +iJMO1TwQwkCD5cSHji3VZAAA +MQLHu8zaCEWNGtVa7Ma/fgAA +fxgsWL9U+02Z6/aBnL7xQAAA + + +PLAY(鏈懡涓) +nr9Lx8SpPkyyyW+S0Q2ciwAA +RUI9oBmwG0GrfVWstzOqIgAA +TTOcitf68U2bRBKTQRVGFwAA +4 +OaxrHODYlUexzn0EoCDzgQAA +mEEUECGIf0awhUt48wkFEAAA +pBxjNtHdF0G5So0dksgHWAAA +y6ZorftPhkKnON2wuA0OmAAA + + +AI +nr9Lx8SpPkyyyW+S0Q2ciwAA +MpCQbaao/0mOmCLX0+jWNgAA +CN3fmVvi+0e8GJKii+qZKwAA +4 +ImU3GP5YZkaO8XZELH0r9QAA +pVljnSKvxUi1KbjMJMunewAA +2pb0tHEJoEWKap4tJ4kfzwAA +80ikBzpBVkalXH2IJpxNaAAA + + +鎬濊冪粨鏉(鏈夊悗鍙版濊) +nr9Lx8SpPkyyyW+S0Q2ciwAA +mbHINkWLDE+1qVAHxNyktQAA +CN3fmVvi+0e8GJKii+qZKwAA +4 +fu/bUTSoYUSgx9dtNLKtDwAA +0TzIenCQ7kqeUdPWz85NEQAA +3Uiu2m/6xke/F4lQ/Y/tfQAA +pC018QDl8E284hzqp+5ORgAA + + +寮曟搸閫鍑 +nr9Lx8SpPkyyyW+S0Q2ciwAA +TTOcitf68U2bRBKTQRVGFwAA +TnN1/tXaskCM+jnEMClKkgAA +4 +1l02CrRXlEe2LWWPqSywuwAA +HCZTJiu4DUqZ2pvWEKKmMQAA +tZ9N1MN5dEuCoMoGDDLHcgAA +GHHodrYVtE63kkNHCZe7GQAA + + + + + + diff --git a/UCCI2QH/makefile.sh b/UCCI2QH/makefile.sh new file mode 100644 index 0000000..a62aad0 --- /dev/null +++ b/UCCI2QH/makefile.sh @@ -0,0 +1 @@ +g++ -DNDEBUG -O4 -Wall -oUCCI2QH.EXE ../base/pipe.cpp ../eleeye/pregen.cpp ../eleeye/position.cpp ../eleeye/genmoves.cpp ucci2qh.cpp \ No newline at end of file diff --git a/UCCI2QH/ucci2qh.cpp b/UCCI2QH/ucci2qh.cpp new file mode 100644 index 0000000..b911930 --- /dev/null +++ b/UCCI2QH/ucci2qh.cpp @@ -0,0 +1,741 @@ +/* +UCCI2QH - a UCCI to Qianhong Protocol Adapter +Designed by Morning Yellow, Version: 2.0, Last Modified: Apr. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include "../base/base2.h" +#include "../base/parse.h" +#include "../base/pipe.h" +#include "../eleeye/position.h" + +const int MAX_CHAR = 1024; // 配置文件的最大长度 +const int MAX_IRREV_POS = 33; // 不可逆局面的最大个数,整个棋局吃子数不会超过32个 +const int MAX_IRREV_MOVE = 200; // 不可逆局面的最大着法数,不吃子着法必须限制在100回合以内 +const int MAX_BAN_MOVE = 128; // 最多的禁止着法数 +const int MAX_INFO = 16; // 版本信息的最大行数 +const int MAX_OPTION = 16; // 选项设置的最大行数 +const int MAX_LEVEL = 16; // 难度的最高级别数 + + +/* 以下常量代表适配器的思考状态,后台思考的处理是一大难点: + * (1) 不启用后台思考时,空闲状态是"IDLE_NONE",正常思考状态是"BUSY_THINK",提示思考状态是"BUSY_HINTS"; + * (2) 启用后台思考时,正常思考结束后,就进入后台思考状态(BUSY_PONDER),而"mvPonder"则是猜测着法; + * (3) "BUSY_PONDER"状态下,如果对手给出的着法没有让后台思考命中,则后台思考中断; + * (4) "BUSY_PONDER"状态下,如果对手给出的着法让后台思考命中(和"mvPonder"一致),就进入后台思考命中状态(BUSY_PONDERHIT); + * (5) "BUSY_PONDER"状态下,如果后台思考结束(在对手给出着法之前),则进入后台思考完成状态(IDLE_PONDER_FINISHED),而"mvPonderFinished"则保存后台思考的结果; + * (6) "BUSY_PONDERHIT"状态下,如果收到思考指令,就转入正常思考状态(BUSY_THINK); + * (7) "BUSY_PONDERHIT"状态下,如果后台思考结束(在对手给出着法之前),就转入后台思考完成并且命中状态(IDLE_PONDERHIT_FINISHED),而"mvPonderFinished"则保存后台思考的结果; + * (8) "IDLE_PONDER_FINISHED"状态下,如果对手给出的着法没有让后台思考命中,则程序重新开始思考。 + * (9) "IDLE_PONDER_FINISHED"状态下,如果对手给出的着法让后台思考命中,就转入后台思考完成并且命中状态(IDLE_PONDERHIT_FINISHED); + * (10) "IDLE_PONDERHIT_FINISHED"状态下,如果收到思考指令,就立即给出"mvPonderFinished"着法; + */ +const int IDLE_NONE = 0; +const int IDLE_PONDER_FINISHED = 1; +const int IDLE_PONDERHIT_FINISHED = 2; +const int BUSY_WAIT = 3; +const int BUSY_THINK = 4; +const int BUSY_HINTS = 5; +const int BUSY_PONDER = 6; +const int BUSY_PONDERHIT = 7; + +static struct { + // 适配器状态选项 + bool bDebug, bUcciOkay, bBgThink; // 是否调试模式,UCCI引擎是否启动,后台思考是否启用 + int nLevel, nStatus; // 级别和状态 + int mvPonder, mvPonderFinished; // 后台思考的猜测着法和后台思考完成的着法 + int mvPonderFinishedPonder; // 后台思考结束后,思考结果的后台思考猜测着法 + // 适配器局面信息 + int nIrrevPosNum; // 当前不可逆局面的个数 + PositionStruct posIrrev[MAX_IRREV_POS]; // 不可逆局面列表,组成了适配器的内部局面 + char szIrrevFen[MAX_IRREV_POS][128]; // 每组不可逆局面的起始局面的FEN串 + int nBanMoveNum; // 禁止着法个数,局面改动后就置零 + int wmvBanList[MAX_BAN_MOVE]; // 禁止着法列表 + // 适配器输入输出通道 + PipeStruct pipeStdin, pipeEngine; // 标准输入(浅红象棋指令)和UCCI引擎管道,参阅"pipe.cpp" + // UCCI引擎配置信息 + char szIniFile[MAX_CHAR]; // 适配器配置文件"UCCI2QH.INI"的全路径 + int nInfoNum, nOptionNum, nLevelNum; // 版本信息行数、选项设置行数和难度级别数 + char szEngineName[MAX_CHAR], szEngineFile[MAX_CHAR]; // UCCI引擎名称和UCCI引擎程序文件的全路径 + char szInfoStrings[MAX_INFO][MAX_CHAR], szOptionStrings[MAX_OPTION][MAX_CHAR]; // 版本信息和选项设置 + char szLevelStrings[MAX_LEVEL][MAX_CHAR], szThinkModes[MAX_LEVEL][MAX_CHAR]; // 难度级别和各个难度级别下的思考模式 +} Ucci2QH; + +// ICCS格式转换为着法结构 +inline int ICCS_MOVE(const char *szIccs) { + int sqSrc, sqDst; + sqSrc = COORD_XY(szIccs[0] - 'A' + FILE_LEFT, '9' + RANK_TOP - szIccs[1]); + sqDst = COORD_XY(szIccs[3] - 'A' + FILE_LEFT, '9' + RANK_TOP - szIccs[4]); + return MOVE(sqSrc, sqDst); +} + +// 着法结构转换为ICCS格式 +inline void MOVE_ICCS(char *szIccs, int mv) { + szIccs[0] = (FILE_X(SRC(mv))) + 'A' - FILE_LEFT; + szIccs[1] = '9' + RANK_TOP - (RANK_Y(SRC(mv))); + szIccs[2] = '-'; + szIccs[3] = (FILE_X(DST(mv))) + 'A' - FILE_LEFT; + szIccs[4] = '9' + RANK_TOP - (RANK_Y(DST(mv))); + szIccs[5] = '\0'; +} + +// 设置适配器状态(在调试模式下,显示该状态) +static void SetStatus(int nArg) { + Ucci2QH.nStatus = nArg; + if (Ucci2QH.bDebug) { + fprintf(stderr, "Adapter Info: Status = "); + switch (nArg) { + case IDLE_NONE: + fprintf(stderr, "IDLE_NONE"); + break; + case IDLE_PONDER_FINISHED: + fprintf(stderr, "IDLE_PONDER_FINISHED"); + break; + case IDLE_PONDERHIT_FINISHED: + fprintf(stderr, "IDLE_PONDERHIT_FINISHED"); + break; + case BUSY_WAIT: + fprintf(stderr, "BUSY_WAIT"); + break; + case BUSY_THINK: + fprintf(stderr, "BUSY_THINK"); + break; + case BUSY_HINTS: + fprintf(stderr, "BUSY_HINTS"); + break; + case BUSY_PONDER: + fprintf(stderr, "BUSY_PONDER"); + break; + case BUSY_PONDERHIT: + fprintf(stderr, "BUSY_PONDERHIT"); + break; + } + fprintf(stderr, "\n"); + fflush(stderr); + } +} + +// 向“浅红象棋”发送反馈信息(在调试模式下显示该信息) +inline void Adapter2QH(const char *szLineStr) { + printf("%s\n", szLineStr); + fflush(stdout); + if (Ucci2QH.bDebug) { + fprintf(stderr, "Adapter->Qianhong: %s\n", szLineStr); + fflush(stderr); + } +} + +// 向UCCI引擎发送指令(在调试模式下显示该信息) +inline void Adapter2UCCI(const char *szLineStr) { + Ucci2QH.pipeEngine.LineOutput(szLineStr); + if (Ucci2QH.bDebug) { + fprintf(stderr, "Adapter->UCCI-Engine: %s\n", szLineStr); + fflush(stderr); + } +} + +// 接收“浅红象棋”的指令 +inline bool QH2Adapter(char *szLineStr) { + if (Ucci2QH.pipeStdin.LineInput(szLineStr)) { + if (Ucci2QH.bDebug) { + fprintf(stderr, "Qianhong->Adapter: %s\n", szLineStr); + fflush(stderr); + } + return true; + } else { + return false; + } +} + +// 接收UCCI引擎的反馈信息 +inline bool UCCI2Adapter(char *szLineStr) { + if (Ucci2QH.pipeEngine.LineInput(szLineStr)) { + if (Ucci2QH.bDebug) { + fprintf(stderr, "UCCI-Engine->Adapter: %s\n", szLineStr); + fflush(stderr); + } + return true; + } else { + return false; + } +} + +// 浅红模式下更新内部局面的过程 +static bool MakeMove(int mv) { + if (mv == 0 || Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum].ucpcSquares[SRC(mv)] == 0) { + return false; + } + if (Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum].ucpcSquares[DST(mv)] == 0) { + // 如果不是吃子着法,那么在上一个可逆局面下执行着法 + if (Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum].nMoveNum < MAX_IRREV_MOVE) { + Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum].MakeMove(mv); + Ucci2QH.nBanMoveNum = 0; + return true; + } else { + return false; + } + } else { + // 如果是吃子着法,那么重新设立一个不可逆局面 + if (Ucci2QH.nIrrevPosNum < MAX_IRREV_POS - 1) { + Ucci2QH.nIrrevPosNum ++; + Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum] = Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum - 1]; + Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum].MakeMove(mv); + Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum].SetIrrev(); + Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum].ToFen(Ucci2QH.szIrrevFen[Ucci2QH.nIrrevPosNum]); + Ucci2QH.nBanMoveNum = 0; + return true; + } else { + return false; + } + } +} + +inline int PieceChar(int pc) { + if (pc < 16) { + return '.'; + } else if (pc < 32) { + return PIECE_BYTE(PIECE_TYPE(pc)); + } else { + return PIECE_BYTE(PIECE_TYPE(pc)) - 'A' + 'a'; + } +} + +// 把局面打印到屏幕上 +static void PrintPosition(const PositionStruct &pos) { + int i, j; + for (i = 3; i <= 12; i ++) { + for (j = 3; j <= 11; j ++) { + printf("%c", PieceChar(pos.ucpcSquares[i * 16 + j])); + } + printf("\n"); + fflush(stdout); + } + if (Ucci2QH.bDebug) { + for (i = 3; i <= 12; i ++) { + fprintf(stderr, "Adapter->Qianhong: "); + for (j = 3; j <= 11; j ++) { + fprintf(stderr, "%c", PieceChar(pos.ucpcSquares[i * 16 + j])); + } + fprintf(stderr, "\n"); + fflush(stderr); + } + } +} + +// 给UCCI引擎发送思考指令 +static void RunEngine(void) { + int i; + uint32_t dwMoveStr; + char *lp; + char szLineStr[LINE_INPUT_MAX_CHAR]; + // 发送思考指令要分三个步骤: + + // 1. 发送局面信息,包括初始的不可逆FEN串和一系列后续着法(连同后台思考的猜测着法); + lp = szLineStr; + lp += sprintf(lp, "position fen %s - - 0 1", Ucci2QH.szIrrevFen[Ucci2QH.nIrrevPosNum]); + if (Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum].nMoveNum > 1) { + lp += sprintf(lp, " moves"); + for (i = 1; i < Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum].nMoveNum; i ++) { + dwMoveStr = MOVE_COORD(Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum].rbsList[i].mvs.wmv); + lp += sprintf(lp, " %.4s", (const char *) &dwMoveStr); + } + } + if (Ucci2QH.nStatus == BUSY_PONDER) { + if (Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum].nMoveNum == 1) { + lp += sprintf(lp, " moves"); + } + dwMoveStr = MOVE_COORD(Ucci2QH.mvPonder); + lp += sprintf(lp, " %.4s", (const char *) &dwMoveStr); + } + Adapter2UCCI(szLineStr); + + // 2. 发送禁止着法信息; + if (Ucci2QH.nBanMoveNum > 0) { + lp = szLineStr; + lp += sprintf(lp, "banmoves"); + for (i = 0; i < Ucci2QH.nBanMoveNum; i ++) { + dwMoveStr = MOVE_COORD(Ucci2QH.wmvBanList[i]); + lp += sprintf(lp, " %.4s", (const char *) &dwMoveStr); + } + Adapter2UCCI(szLineStr); + } + + // 3. 发送思考指令。 + sprintf(szLineStr, Ucci2QH.nStatus == BUSY_PONDER ? "go ponder %s" : "go %s", Ucci2QH.szThinkModes[Ucci2QH.nLevel]); + Adapter2UCCI(szLineStr); +} + +// UCCI反馈信息的接收过程 +static bool ReceiveUCCI(void) { + int mv; + char *lp; + char szIccs[8]; + char szLineStr[LINE_INPUT_MAX_CHAR]; + if (!UCCI2Adapter(szLineStr)) { + return false; + } + lp = szLineStr; + if (Ucci2QH.bUcciOkay) { + if (StrEqvSkip(lp, "bestmove ")) { + mv = COORD_MOVE(*(uint32_t *) lp); + lp += sizeof(uint32_t); + switch (Ucci2QH.nStatus) { + // 一旦收到反馈着法,就根据"bStatus"决定相应的处理过程,并转入空闲状态: + + // 1. "BUSY_WAIT"状态,说明是由"StopEngine()"中断的,不作任何处理; + case BUSY_WAIT: + SetStatus(IDLE_NONE); + break; + + // 2. "BUSY_THINK"状态,输出并执行最佳着法,并视有无后台思考猜测着法作出相应处理; + case BUSY_THINK: + MOVE_ICCS(szIccs, mv); + Adapter2QH(szIccs); + MakeMove(mv); + if (Ucci2QH.bBgThink && StrEqvSkip(lp, " ponder ")) { + Ucci2QH.mvPonder = COORD_MOVE(*(uint32_t *) lp); + SetStatus(BUSY_PONDER); + RunEngine(); + } else { + SetStatus(IDLE_NONE); + } + break; + + // 3. "BUSY_HINTS"状态,只要输出最佳着法即可; + case BUSY_HINTS: + MOVE_ICCS(szIccs, mv); + Adapter2QH(szIccs); + Adapter2QH("ENDHINTS"); + SetStatus(IDLE_NONE); + break; + + // 4. "BUSY_PONDER"和"BUSY_PONDERHIT"状态,只要将最佳着法记录为后台思考结果即可; + case BUSY_PONDER: + case BUSY_PONDERHIT: + Ucci2QH.mvPonderFinished = mv; + SetStatus(Ucci2QH.nStatus == BUSY_PONDER ? IDLE_PONDER_FINISHED : IDLE_PONDERHIT_FINISHED); + if (Ucci2QH.bBgThink && StrEqvSkip(lp, " ponder ")) { + Ucci2QH.mvPonderFinishedPonder = COORD_MOVE(*(uint32_t *) lp); + } else { + Ucci2QH.mvPonderFinishedPonder = 0; + } + break; + default: + break; + }; + } else if (StrEqv(lp, "nobestmove")) { + + // 5. 最后考虑没有最佳着法的情况。 + switch (Ucci2QH.nStatus) { + case BUSY_WAIT: + break; + case BUSY_HINTS: + case BUSY_THINK: + Adapter2QH("ERROR"); + break; + case BUSY_PONDER: + case BUSY_PONDERHIT: + break; + default: + break; + } + SetStatus(IDLE_NONE); + + } else if (StrEqv(lp, "bye")) { + Ucci2QH.bUcciOkay = false; + } + } else { + if (StrEqv(lp, "ucciok")) { + Ucci2QH.bUcciOkay = true; + } + } + return true; +} + +// 中止UCCI引擎的思考 +static void StopEngine(void) { + int64_t llTime; + SetStatus(BUSY_WAIT); + Adapter2UCCI("stop"); + llTime = GetTime(); + while (Ucci2QH.nStatus != IDLE_NONE && (int) (GetTime() - llTime) < 1000) { + if (!ReceiveUCCI()) { + Idle(); + } + } + Ucci2QH.nStatus = IDLE_NONE; +} + +// 浅红象棋指令的接收过程 +static bool ReceiveQH(void) { + int i, j; + int mv; + int64_t llTime; + char *lp; + char szIccs[8]; + char szLineStr[LINE_INPUT_MAX_CHAR]; + + if (!QH2Adapter(szLineStr)) { + return false; + } + lp = szLineStr; + if (false) { + // 浅红象棋协议接收到的指令大致有以下几种: + + // 1. "SCR"指令(略); + } else if (StrEqv(lp, "SCR")) { + PrintPosition(Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum]); + + // 2. "LEVEL"指令(略); + } else if (StrEqvSkip(lp, "LEVEL ")) { + Ucci2QH.nLevel = Str2Digit(lp, 0, Ucci2QH.nLevelNum - 1); + Adapter2QH("OK"); + // 注意:必须首先判断"LEVEL ",再判断"LEVEL" + } else if (StrEqv(lp, "LEVEL")) { + sprintf(szLineStr, "%d", Ucci2QH.nLevelNum); + Adapter2QH(szLineStr); + + // 3. "FEN"指令,更新内部局面,并且清除后台思考状态; + } else if (StrEqvSkip(lp, "FEN ")) { + if (Ucci2QH.nStatus == BUSY_THINK || Ucci2QH.nStatus == BUSY_HINTS) { + Adapter2QH("ERROR"); + return true; + } + Ucci2QH.nIrrevPosNum = 0; + Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum].FromFen(lp); + Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum].ToFen(Ucci2QH.szIrrevFen[Ucci2QH.nIrrevPosNum]); + if (Ucci2QH.nStatus == BUSY_PONDER || Ucci2QH.nStatus == BUSY_PONDERHIT) { + StopEngine(); + } else { + SetStatus(IDLE_NONE); + } + Adapter2UCCI("setoption newgame"); + Adapter2QH("OK"); + + // 4. "PLAY"指令; + } else if (StrEqvSkip(lp, "PLAY ")) { + if (Ucci2QH.nStatus == BUSY_THINK || Ucci2QH.nStatus == BUSY_HINTS) { + Adapter2QH("ERROR"); + return true; + } + mv = ICCS_MOVE(lp); + if (!MakeMove(mv)) { + Adapter2QH("ERROR"); + return true; + } + // 至此着法执行完毕,以下是更改后台思考状态 + switch (Ucci2QH.nStatus) { + case IDLE_PONDER_FINISHED: + SetStatus(mv == Ucci2QH.mvPonder ? IDLE_PONDERHIT_FINISHED : IDLE_NONE); + break; + case IDLE_PONDERHIT_FINISHED: + SetStatus(IDLE_NONE); + break; + case BUSY_PONDER: + if (mv == Ucci2QH.mvPonder) { + SetStatus(BUSY_PONDERHIT); + Adapter2UCCI("ponderhit"); + } else { + StopEngine(); + } + break; + case BUSY_PONDERHIT: + StopEngine(); + break; + default: + break; + } + Adapter2QH("OK"); + + // 5. "LOAD"指令,逐一载入着法,并且清除后台思考状态; + } else if (StrEqvSkip(lp, "LOAD ")) { + i = Str2Digit(lp, 0, 1998); // 一局棋最多有999个回合,即1998个着法 + if (Ucci2QH.nStatus == BUSY_THINK || Ucci2QH.nStatus == BUSY_HINTS) { + for (j = 0; j < i; j ++) { + while (!QH2Adapter(szLineStr)) { + Idle(); + } + } + Adapter2QH("ERROR"); + return true; + } + for (j = 0; j < i; j ++) { + while (!QH2Adapter(szLineStr)) { + Idle(); + } + mv = ICCS_MOVE(szLineStr); + MakeMove(mv); + } + if (Ucci2QH.nStatus == BUSY_PONDER || Ucci2QH.nStatus == BUSY_PONDERHIT) { + StopEngine(); + } else { + SetStatus(IDLE_NONE); + } + Adapter2QH("OK"); + + // 6. "AI"指令,进入思考状态; + } else if (StrEqv(lp, "AI")) { + if (Ucci2QH.nStatus == BUSY_THINK || Ucci2QH.nStatus == BUSY_HINTS) { + Adapter2QH("ERROR"); + return true; + } + switch (Ucci2QH.nStatus) { + case IDLE_NONE: + case IDLE_PONDER_FINISHED: + SetStatus(BUSY_THINK); + RunEngine(); + break; + case IDLE_PONDERHIT_FINISHED: + MakeMove(Ucci2QH.mvPonderFinished); + MOVE_ICCS(szIccs, Ucci2QH.mvPonderFinished); + Adapter2QH(szIccs); + if (Ucci2QH.mvPonderFinishedPonder == 0) { + SetStatus(IDLE_NONE); + } else { + Ucci2QH.mvPonder = Ucci2QH.mvPonderFinishedPonder; + SetStatus(BUSY_PONDER); + RunEngine(); + } + break; + case BUSY_PONDER: + StopEngine(); + SetStatus(BUSY_THINK); + RunEngine(); + break; + case BUSY_PONDERHIT: + SetStatus(BUSY_THINK); + break; + default: + break; + } + + // 7. "ABORT"指令(略); + } else if (StrEqv(lp, "ABORT")) { + StopEngine(); + Adapter2QH("ABORTED"); + + // 8. "QUIT"指令(略); + } else if (StrEqv(lp, "QUIT")) { + if (Ucci2QH.nStatus > BUSY_WAIT) { + StopEngine(); + } + Adapter2UCCI("quit"); + llTime = GetTime(); + while (Ucci2QH.bUcciOkay && (int) (GetTime() - llTime) < 1000) { + if (!ReceiveUCCI()) { + Idle(); + } + } + Ucci2QH.bUcciOkay = false; + + // 9. "UNDO"指令,撤消着法,并且清除后台思考状态; + } else if (StrEqv(lp, "UNDO")) { + if (Ucci2QH.nStatus == BUSY_THINK || Ucci2QH.nStatus == BUSY_HINTS) { + Adapter2QH("ERROR"); + return true; + } + if (Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum].nMoveNum == 1) { + if (Ucci2QH.nIrrevPosNum == 0) { + Adapter2QH("ERROR"); + return true; + } + Ucci2QH.nIrrevPosNum --; + } else { + Ucci2QH.posIrrev[Ucci2QH.nIrrevPosNum].UndoMakeMove(); + } + if (Ucci2QH.nStatus == BUSY_PONDER || Ucci2QH.nStatus == BUSY_PONDERHIT) { + StopEngine(); + } else { + SetStatus(IDLE_NONE); + } + Adapter2QH("OK"); + + // 10. "HINTS"指令,给出提示; + } else if (StrEqv(lp, "HINTS")) { + if (Ucci2QH.nStatus == BUSY_THINK || Ucci2QH.nStatus == BUSY_HINTS) { + Adapter2QH("ERROR"); + return true; + } + if (Ucci2QH.nStatus == BUSY_PONDER || Ucci2QH.nStatus == BUSY_PONDERHIT) { + // 如果正在后台思考,则输出后台思考的猜测着法,作为提示着法 + MOVE_ICCS(szIccs, Ucci2QH.mvPonder); + Adapter2QH(szIccs); + Adapter2QH("ENDHINTS"); + } else { + // 其他情况,让引擎思考一个提示着法 + SetStatus(BUSY_HINTS); + RunEngine(); + } + + // 11. "BAN"指令,读入禁止着法到"Ucci2QH.wmvBanList"就可以了; + } else if (StrEqvSkip(lp, "BAN ")) { + Ucci2QH.nBanMoveNum = Str2Digit(lp, 0, MAX_BAN_MOVE); + for (i = 0; i < Ucci2QH.nBanMoveNum; i ++) { + while (!QH2Adapter(szLineStr)) { + Idle(); + } + Ucci2QH.wmvBanList[i] = ICCS_MOVE(szLineStr); + } + if (Ucci2QH.nStatus == BUSY_PONDER || Ucci2QH.nStatus == BUSY_PONDERHIT) { + StopEngine(); + } else { + SetStatus(IDLE_NONE); + } + Adapter2QH("OK"); + + // 12. "BGTHINK"指令(略); + } else if (StrEqv(lp, "BGTHINK ON")) { + Ucci2QH.bBgThink = true; + Adapter2QH("OK"); + } else if (StrEqv(lp, "BGTHINK OFF")) { + Ucci2QH.bBgThink = false; + Adapter2QH("OK"); + + // 13. "TIMEOUT"指令(略)。 + } else if (StrEqv(lp, "TIMEOUT")) { + Adapter2UCCI("stop"); + } + return true; +} + +// 主函数 +int main(int argc, char **argv) { + int64_t llTime; + char szLineStr[MAX_CHAR]; + char *lp; + FILE *fpIniFile; + int i, nCurrLevel; + + if (argc < 2) { + return 0; + } + LocatePath(Ucci2QH.szIniFile, "UCCI2QH.INI"); + nCurrLevel = Ucci2QH.nLevelNum = Ucci2QH.nInfoNum = 0; + Ucci2QH.szEngineName[0] = Ucci2QH.szEngineFile[0] = '\0'; + fpIniFile = fopen(Ucci2QH.szIniFile, "rt"); + if (fpIniFile == NULL) { + return 0; + } + while (fgets(szLineStr, MAX_CHAR, fpIniFile) != NULL) { + StrCutCrLf(szLineStr); + lp = szLineStr; + if (false) { + } else if (StrEqvSkip(lp, "Name=")) { + strcpy(Ucci2QH.szEngineName, lp); + } else if (StrEqvSkip(lp, "File=")) { + LocatePath(Ucci2QH.szEngineFile, lp); + } else if (StrEqvSkip(lp, "Info=")) { + if (Ucci2QH.nLevelNum < MAX_INFO) { + strcpy(Ucci2QH.szInfoStrings[Ucci2QH.nInfoNum], lp); + Ucci2QH.nInfoNum ++; + } + } else if (StrEqvSkip(lp, "Option=")) { + if (Ucci2QH.nOptionNum < MAX_OPTION) { + strcpy(Ucci2QH.szOptionStrings[Ucci2QH.nOptionNum], lp); + Ucci2QH.nOptionNum ++; + } + } else if (StrEqvSkip(lp, "Level=")) { + if (Ucci2QH.nLevelNum < MAX_LEVEL) { + strcpy(Ucci2QH.szLevelStrings[Ucci2QH.nLevelNum], lp); + Ucci2QH.nLevelNum ++; + } + } else if (StrEqvSkip(lp, "ThinkMode=")) { + if (nCurrLevel < Ucci2QH.nLevelNum) { + strcpy(Ucci2QH.szThinkModes[nCurrLevel], lp); + nCurrLevel ++; + } + } + } + fclose(fpIniFile); + for (; nCurrLevel < Ucci2QH.nLevelNum; nCurrLevel ++) { + Ucci2QH.szThinkModes[nCurrLevel][0] = '\0'; + } + + if (false) { + // 浅红引擎有以下两种命令格式: + + // 1. 启动引擎:UCCI2QH -plugin [debug] + } else if (StrEqv(argv[1], "-plugin")) { + Ucci2QH.bDebug = Ucci2QH.bUcciOkay = Ucci2QH.bBgThink = false; + Ucci2QH.nLevel = 0; + SetStatus(IDLE_NONE); + if (argc > 2) { + if (StrEqv(argv[2], "debug")) { + Ucci2QH.bDebug = true; + } + } + Ucci2QH.pipeStdin.Open(); + Ucci2QH.pipeEngine.Open(Ucci2QH.szEngineFile); + Adapter2UCCI("ucci"); + PreGenInit(); + Ucci2QH.nIrrevPosNum = 0; + strcpy(Ucci2QH.szIrrevFen[0], cszStartFen); + Ucci2QH.posIrrev[0].FromFen(Ucci2QH.szIrrevFen[0]); + llTime = GetTime(); + // 等待10秒钟,如果引擎无法正常启动,就直接退出。 + while (!Ucci2QH.bUcciOkay && (int) (GetTime() - llTime) < 10000) { + if (!ReceiveUCCI()) { + Idle(); + } + } + if (Ucci2QH.bUcciOkay) { + for (i = 0; i < Ucci2QH.nOptionNum; i ++) { + Adapter2UCCI(Ucci2QH.szOptionStrings[i]); + } + Adapter2UCCI("setoption newgame"); + } + while (Ucci2QH.bUcciOkay) { + if (!(ReceiveUCCI() || ReceiveQH())) { + Idle(); + } + } + Ucci2QH.pipeEngine.Close(); + Adapter2QH("BYE"); + + // 2. 显示引擎信息:UCCI2QH -info + } else if (StrEqv(argv[1], "-info")) { + printf("QHPLUGIN V1.3\n"); + printf("%s\n", Ucci2QH.szEngineName); + printf("LEVELS %d\n", Ucci2QH.nLevelNum); + for (i = 0; i < Ucci2QH.nLevelNum; i ++) { + printf("%d - %s\n", i, Ucci2QH.szLevelStrings[i]); + } + printf("UNDO 1\n"); + printf("HINTS 1\n"); + printf("RULES 1\n"); + printf("BGTHINK 1\n"); + printf("TIMEOUT 1\n"); + for (i = 0; i < Ucci2QH.nInfoNum; i ++) { + printf("%s\n", Ucci2QH.szInfoStrings[i]); + } + printf("=== UCCI Engine Options ===\n"); + printf("Engine File: %s\n", Ucci2QH.szEngineFile); + printf("Option List:\n"); + for (i = 0; i < Ucci2QH.nOptionNum; i ++) { + printf("%s\n", Ucci2QH.szOptionStrings[i]); + } + printf("Level List:\n"); + for (i = 0; i < Ucci2QH.nLevelNum; i ++) { + printf("%s=\"go [ponder] %s\"\n", Ucci2QH.szLevelStrings[i], Ucci2QH.szThinkModes[i]); + } + printf("ENDINFO\n"); + fflush(stdout); + } + return 0; +} diff --git a/XQFTOOLS/ECCO.DLL b/XQFTOOLS/ECCO.DLL new file mode 100644 index 0000000..1b1eee1 Binary files /dev/null and b/XQFTOOLS/ECCO.DLL differ diff --git a/XQFTOOLS/MAKEFILE.BAT b/XQFTOOLS/MAKEFILE.BAT new file mode 100644 index 0000000..8808f0c --- /dev/null +++ b/XQFTOOLS/MAKEFILE.BAT @@ -0,0 +1,17 @@ +@ECHO OFF +RC ..\RES\XQF2PGN.RC +RC ..\RES\PGN2XQF.RC +RC ..\RES\MXQ2PGN.RC +RC ..\RES\CHE2PGN.RC +RC ..\RES\CHN2PGN.RC +RC ..\RES\CCM2PGN.RC +RC ..\RES\MXQFCONV.RC +CL /DNDEBUG /O2 /W3 /Fe..\BIN\XQF2PGN.EXE ..\ELEEYE\PREGEN.CPP ..\ELEEYE\POSITION.CPP ..\ELEEYE\GENMOVES.CPP ..\CCHESS\CCHESS.CPP ..\CCHESS\PGNFILE.CPP XQF2PGN.CPP SHELL32.LIB ..\RES\XQF2PGN.RES +CL /DNDEBUG /O2 /W3 /Fe..\BIN\PGN2XQF.EXE ..\ELEEYE\PREGEN.CPP ..\ELEEYE\POSITION.CPP ..\ELEEYE\GENMOVES.CPP ..\CCHESS\CCHESS.CPP ..\CCHESS\PGNFILE.CPP PGN2XQF.CPP SHELL32.LIB ..\RES\PGN2XQF.RES +CL /DNDEBUG /O2 /W3 /Fe..\BIN\MXQ2PGN.EXE ..\ELEEYE\PREGEN.CPP ..\ELEEYE\POSITION.CPP ..\ELEEYE\GENMOVES.CPP ..\CCHESS\CCHESS.CPP ..\CCHESS\PGNFILE.CPP MXQ2PGN.CPP SHLWAPI.LIB SHELL32.LIB ..\RES\MXQ2PGN.RES +CL /DNDEBUG /O2 /W3 /Fe..\BIN\CHE2PGN.EXE ..\ELEEYE\PREGEN.CPP ..\ELEEYE\POSITION.CPP ..\ELEEYE\GENMOVES.CPP ..\CCHESS\CCHESS.CPP ..\CCHESS\PGNFILE.CPP CHE2PGN.CPP SHELL32.LIB ..\RES\CHE2PGN.RES +CL /DNDEBUG /O2 /W3 /Fe..\BIN\CHN2PGN.EXE ..\ELEEYE\PREGEN.CPP ..\ELEEYE\POSITION.CPP ..\ELEEYE\GENMOVES.CPP ..\CCHESS\CCHESS.CPP ..\CCHESS\PGNFILE.CPP CHN2PGN.CPP SHELL32.LIB ..\RES\CHN2PGN.RES +CL /DNDEBUG /O2 /W3 /Fe..\BIN\CCM2PGN.EXE ..\ELEEYE\PREGEN.CPP ..\ELEEYE\POSITION.CPP ..\ELEEYE\GENMOVES.CPP ..\CCHESS\CCHESS.CPP ..\CCHESS\PGNFILE.CPP CCM2PGN.CPP SHELL32.LIB ..\RES\CCM2PGN.RES +CL /DNDEBUG /O2 /W3 /DMXQFCONV_EXE /Fe..\BIN\MXQFCONV.EXE ..\ELEEYE\PREGEN.CPP ..\ELEEYE\POSITION.CPP ..\ELEEYE\GENMOVES.CPP ..\CCHESS\CCHESS.CPP ..\CCHESS\PGNFILE.CPP XQF2PGN.CPP MXQ2PGN.CPP CHE2PGN.CPP CHN2PGN.CPP CCM2PGN.CPP MXQFCONV.CPP USER32.LIB SHLWAPI.LIB SHELL32.LIB ..\RES\MXQFCONV.RES +DEL ..\RES\*.RES +DEL *.OBJ \ No newline at end of file diff --git a/XQFTOOLS/MXQFCONV.CPP b/XQFTOOLS/MXQFCONV.CPP new file mode 100644 index 0000000..0480691 --- /dev/null +++ b/XQFTOOLS/MXQFCONV.CPP @@ -0,0 +1,124 @@ +/* +MXQ/XQF/CHE/CHN/CCM->PGN Convertor - a Chinese Chess Score Convertion Program +Designed by Morning Yellow, Version: 3.14, Last Modified: Jun. 2008 +Copyright (C) 2004-2008 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include "../base/base2.h" +#include "../eleeye/pregen.h" +#include "../cchess/cchess.h" +#include "../cchess/ecco.h" + +int Xqf2Pgn(const char *szXqfFile, const char *szPgnFile, const EccoApiStruct &EccoApi); +int Mxq2Pgn(const char *szMxqFile, const char *szPgnFile, const EccoApiStruct &EccoApi); +int Che2Pgn(const char *szCheFile, const char *szPgnFile, const EccoApiStruct &EccoApi); +int Chn2Pgn(const char *szChnFile, const char *szPgnFile, const EccoApiStruct &EccoApi); +int Ccm2Pgn(const char *szCcmFile, const char *szPgnFile, const EccoApiStruct &EccoApi); + +static void SearchFolder(const char *szFolderPath, const EccoApiStruct &EccoApi); + +static void SearchFile(const char *szFilePath, const WIN32_FIND_DATA &wfd, const EccoApiStruct &EccoApi) { + int nPathLen; + char szPgnFile[1024]; + if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + nPathLen = strlen(szFilePath) - 4; + if (nPathLen > 0) { + if (false) { + } else if (strnicmp(szFilePath + nPathLen, ".XQF", 4) == 0) { + strncpy(szPgnFile, szFilePath, nPathLen); + strcpy(szPgnFile + nPathLen, ".PGN"); + Xqf2Pgn(szFilePath, szPgnFile, EccoApi); + } else if (strnicmp(szFilePath + nPathLen, ".MXQ", 4) == 0) { + strncpy(szPgnFile, szFilePath, nPathLen); + strcpy(szPgnFile + nPathLen, ".PGN"); + Mxq2Pgn(szFilePath, szPgnFile, EccoApi); + } else if (strnicmp(szFilePath + nPathLen, ".CHE", 4) == 0) { + strncpy(szPgnFile, szFilePath, nPathLen); + strcpy(szPgnFile + nPathLen, ".PGN"); + Che2Pgn(szFilePath, szPgnFile, EccoApi); + } else if (strnicmp(szFilePath + nPathLen, ".CHN", 4) == 0) { + strncpy(szPgnFile, szFilePath, nPathLen); + strcpy(szPgnFile + nPathLen, ".PGN"); + Chn2Pgn(szFilePath, szPgnFile, EccoApi); + } else if (strnicmp(szFilePath + nPathLen, ".CCM", 4) == 0) { + strncpy(szPgnFile, szFilePath, nPathLen); + strcpy(szPgnFile + nPathLen, ".PGN"); + Ccm2Pgn(szFilePath, szPgnFile, EccoApi); + } + } + } else { + if (strcmp(wfd.cFileName, ".") != 0 && strcmp(wfd.cFileName, "..") != 0) { + SearchFolder(szFilePath, EccoApi); + } + } +} + +static void SearchFolder(const char *szFolderPath, const EccoApiStruct &EccoApi) { + char szFilePath[1024]; + WIN32_FIND_DATA wfd; + HANDLE hFind; + char *lpFilePath; + + strcpy(szFilePath, szFolderPath); + lpFilePath = szFilePath + strlen(szFolderPath); + if (*(lpFilePath - 1) == '\\') { + lpFilePath --; + } + strcpy(lpFilePath, "\\*"); + lpFilePath ++; + hFind = FindFirstFile(szFilePath, &wfd); + if (hFind != INVALID_HANDLE_VALUE) { + strcpy(lpFilePath, wfd.cFileName); + SearchFile(szFilePath, wfd, EccoApi); + while (FindNextFile(hFind, &wfd)) { + strcpy(lpFilePath, wfd.cFileName); + SearchFile(szFilePath, wfd, EccoApi); + } + } + FindClose(hFind); +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { + char szPath[MAX_PATH], szLibEccoFile[MAX_PATH]; + EccoApiStruct EccoApi; + BROWSEINFO bi; + LPITEMIDLIST pidl; + + bi.hwndOwner = NULL; + bi.pidlRoot = NULL; + bi.pszDisplayName = NULL; + bi.lpszTitle = "请选择象棋演播室棋谱(*.XQF)、弈天棋谱(*.MXQ)、QQ象棋棋谱(*.CHE)、联众象棋棋谱(*.CHN)和中游象棋棋谱(*.CCM)所在的文件夹"; + bi.ulFlags = BIF_RETURNONLYFSDIRS; + bi.lpfn = NULL; + bi.lParam = NULL; + bi.iImage = 0; + pidl = SHBrowseForFolder(&bi); + if (SHGetPathFromIDList(pidl, szPath)) { + PreGenInit(); + ChineseInit(); + LocatePath(szLibEccoFile, cszLibEccoFile); + EccoApi.Startup(szLibEccoFile); + SearchFolder(szPath, EccoApi); + EccoApi.Shutdown(); + MessageBox(NULL, "象棋演播室棋谱(*.XQF)、弈天棋谱(*.MXQ)、QQ象棋棋谱(*.CHE)、联众象棋棋谱(*.CHN)和中游象棋棋谱(*.CCM)已全部转换为可移植棋谱(*.PGN)。", + "象棋棋谱批量转换工具", MB_ICONINFORMATION); + } + return 0; +} \ No newline at end of file diff --git a/XQFTOOLS/README.TXT b/XQFTOOLS/README.TXT new file mode 100644 index 0000000..7adf6a1 --- /dev/null +++ b/XQFTOOLS/README.TXT @@ -0,0 +1,25 @@ +XQF工具包——XQF->PGN和PGN->XQF转换器 + +(1) XQF->PGN转换器 + +  “XQF->PGN转换器”是第一个真正意义上的XQF文件转换工具,它实现了XQF文件转换操作的快速化和批量化。在安装了象棋百科全书网(www.elephantbase.net)提供的XQF工具包后,XQF文件就自动和XQF->PGN转换器建立了联系,只要右击XQF文件,在弹出菜单中选择“Open with PGN Reader”,转换后的文件就会自动被PGN文件默认的关联程序所打开(通常是《象棋巫师》,也可以让PGN文件与普通文本编辑器关联起来)。 +  在命令行方式下,还可以实现批量XQF文件到PGN文件的转换。例如输入下列命令: + +FOR %A IN (*.XQF) DO XQF2PGN "%A" "%A.PGN" + +  需要提醒的是,XQF->PGN转换器发布时,XQStudio的最新版本是1.63,因此XQF->PGN转换器不能保证以后版本的XQStudio产生的XQF文件被正常转换。 + +(2) PGN->XQF转换器 + +  如果说加密的XQF格式是一道门槛的话,那么XQStudio的作者“过河象”在网上棋牌乐(www.qilaile.net)上公布的非加密XQF格式(即XQF1.0格式)无疑使这道门槛降低了,这使得任何版本的XQStudio可以读取任何格式的棋谱文件,只要能把棋谱转换成非加密XQF格式即可。 +  “PGN->XQF转换器”是为方便XQStudio用户而制作的程序,安装象棋百科全书网提供的XQF工具包,即可很方便地打开任何PGN文件(跟XQF->PGN转换器的用法一样,只要右击PGN文件,在弹出菜单中选择“Open with XQF Reader”即可)。 +  在命令行方式下,还可以实现批量PGN文件到XQF文件的转换。例如输入下列命令: + +FOR %A IN (*.PGN) DO PGN2XQF "%A" "%A.XQF" + +(3) 补充说明: + +  XQF工具包(附带XQStudio 1.63)的下载地址是:http://www.elephantbase.net/download/xqftools.exe。 +  XQF工具包的源代码是象棋程序 ElephantEye 的源代码的一部分,发布在 SourceForge 上:http://sourceforge.net/projects/elephanteye/。 +  若有使用方法上的疑问,请联系XQF工具包的发布者:webmaster@elephantbase.net。 +  感谢网友“小月儿”(xiaoyueer2000@hotmail.com)提供了XQF文件的解密算法,本程序是根据该算法设计的。 diff --git a/XQFTOOLS/SAMPLE.PGN b/XQFTOOLS/SAMPLE.PGN new file mode 100644 index 0000000..ce2dff9 --- /dev/null +++ b/XQFTOOLS/SAMPLE.PGN @@ -0,0 +1,71 @@ +[Game "Chinese Chess"] +[Event "许银川让九子对聂棋圣"] +[Round ""] +[Date "1999.12.09"] +[Site "广州"] +[RedTeam ""] +[Red "许银川"] +[BlackTeam ""] +[Black "聂卫平"] +[Result "1-0"] +[ECCO ""] +[Opening ""] +[Variation ""] +[FEN "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/9/1C5C1/9/RN2K2NR w - - 0 1"] +{  象棋让九子原属茶余饭后的娱乐,不意今日却被摆上赛桌,更为离奇的是:我的对手竟是在围棋棋坛上叱咤风云的聂大帅。赛前我并不了解对手的实力,但相信以聂棋圣在围棋上所体现出来的过人智慧,必能在棋理上触类旁通。因此我在赛前也作了一些准备,在对局中更是小心翼翼,不敢掉以轻心。 +  许银川让去5只兵和双士双相,执红先行。} + 1. 炮八平五 炮8平5 +{  红方首着架中炮必走之着,聂棋圣还架中炮拼兑子力,战术对头。} + 2. 炮五进五 象7进5 + 3. 炮二平五 +{  再架中炮也属正着,如改走马八进七,则象5退7,红方帅府受攻,当然若红方仍再架中炮拼兑,那么失去双炮就难有作用了。} + 3. 马8进7 + 4. 马二进三 车9平8 + 5. 马八进七 马2进1 + 6. 车九平六 车1平2 +{  聂棋圣仍按常规战法出动主力,却忽略了红方车塞象眼的凶着,应走车1进1。} + 7. 车六进八 +{  红车疾点象眼,局势霎时有剑拔弩张之感。这种对弈不能以常理揣度,红方只能像程咬金的三板斧一般猛攻一轮,若黑方防守得法则胜负立判。} + 7. 炮2进7 +{  却说聂棋圣见我来势汹汹,神色顿时颇为凝重,一番思索之后沉下底炮以攻为守,果是身手不凡。此着如改走炮2平3,则帅五平六,炮3进5,车六进一,将5进1,炮五退二,黑方不易驾驭局面。} + 8. 车一进四 炮2平1 + 9. 马七进八 炮1退4 + 10. 马八退七 炮1进4 + 11. 马七进八 车2进2 +{  其实黑方仍可走炮1退4,红方若续走马八退七,则仍炮1进4不变作和,因黑右车叫将红可车六退九,故不算犯规。} + 12. 炮五平八 炮1退4 +{  劣着,导致失子,应走车2平3,红方如马八进六,则车3退1,红方无从着手。但有一点必须注意,黑车躲进暗道似与棋理相悖,故聂棋圣弃子以求局势缓和情有可原。} + 13. 炮八进五 炮1平9 + 14. 炮八平三 车8进2 + 15. 炮三进一 车8进2 + 16. 马八进六 炮9平5 + 17. 炮三平一 士6进5 + 18. 马六进四 车8平5 + 19. 帅五平六 +{  可直接走马四进三叫将再踩中象。} + 19. 车5平6 + 20. 马四进三 将5平6 + 21. 车六退四 卒5进1 + 22. 车六进二 炮5平7 + 23. 前马退二 象5进7 + 24. 马二退三 卒5进1 + 25. 车六平三 卒5平6 + 26. 车三进三 将6进1 + 27. 后马进二 士5进6 + 28. 马二进三 将6平5 + 29. 前马进二 +{  红方有些拖沓,应直接走车三平六立成绝杀。} + 29. 将5进1 + 30. 车三平六 士6退5 + 31. 马二退三 车6退1 + 32. 车六退三 +{  再擒一车,以下着法仅是聊尽人事而已。} + 32. 车6平7 + 33. 车六平三 卒6平7 + 34. 车三平五 将5平6 + 35. 帅六平五 将6退1 + 36. 车五进二 将6退1 + 37. 车五进一 将6进1 + 38. 车五平七 +{  至此,聂棋圣认负。与此同时,另一盘围棋对弈我被屠去一条大龙,已无力再战,遂平分秋色,皆大欢喜。} + 1-0 diff --git a/XQFTOOLS/SAMPLE.XQF b/XQFTOOLS/SAMPLE.XQF new file mode 100644 index 0000000..b4e98fb Binary files /dev/null and b/XQFTOOLS/SAMPLE.XQF differ diff --git a/XQFTOOLS/XQBITMAP.ZIP b/XQFTOOLS/XQBITMAP.ZIP new file mode 100644 index 0000000..6ac1418 Binary files /dev/null and b/XQFTOOLS/XQBITMAP.ZIP differ diff --git a/XQFTOOLS/XQF.TXT b/XQFTOOLS/XQF.TXT new file mode 100644 index 0000000..923267e --- /dev/null +++ b/XQFTOOLS/XQF.TXT @@ -0,0 +1,361 @@ + 象棋演播室(XQStudio) 1.0 版XQF文件格式说明 + + (XQF 1.0) + + (过河象, xqstudio@qipaile.net) + + 2001年4月16日 + +一、说明 + + 本文件格式是XQStudio 1.0所使用的文件格式,现公布该格式的主要目的是为了 +方便程序员将棋谱数据保存为XQF文件格式,以便借助于XQStudio的打谱功能。 各下 +棋网站或对弈软件均可将棋谱保存为此格式,这样XQStudio的任意版本均可以使用该 +文件。 + 由于XQStudio 1.0的后续版本使用了更为复杂的文件格式,使用该格式不保证能 +读出XQStudio更高版本的XQF文件。换句话说,该格式只保证可以产生一个正确的XQF +文件,不保证所有的XQF文件都是该格式。 + + +二、XQF文件使用的棋盘坐标 + + 在XQF文件中,用数字坐标表示棋盘上交叉点的位置,其中X坐标从0到8,Y坐标从 +0到9,坐标的原点(0,0)在棋盘的左下角。如下图,黑方的将在位置(4,9)处,红方的 +帅在位置(4,0)处。 + + Y   09 19 29 39 49 59 69 79 89         + 9 [车][马][象][士][将][士][象][马][车] +  │ │ │ │\│/│ │ │ │  + 8  ├─┼─┼─┼─※─┼─┼─┼─┤  +  │ │ │ │/│\│ │ │ │  + 7  ├-[炮]-┼─┼─┼─┼─┼-[炮]-┤  +  │ │ │ │ │ │ │ │ │  + 6 [卒]-┼-[卒]-┼-[卒]-┼-[卒]-┼-[卒] +  │ │ │ │ │ │ │ │ │  + 5  ├─┴─┴─┴─┴─┴─┴─┴─┤  +  │               │  + 4  ├─┬─┬─┬─┬─┬─┬─┬─┤  +  │ │ │ │ │ │ │ │ │  + 3 (兵)-┼-(兵)-┼-(兵)-┼-(兵)-┼-(兵) +  │ │ │ │ │ │ │ │ │  + 2  ├-(炮)-┼─┼─┼─┼─┼-(炮)-┤  +  │ │ │ │\│/│ │ │ │  + 1  ├─┼─┼─┼─※─┼─┼─┼─┤  +  │ │ │ │/│\│ │ │ │  + 0 (车)(马)(相)(士)(帅)(士)(相)(马)(车) +    00 10 20 30 40 50 60 70 80  + X-> 0 1 2 3 4 5 6 7 8 + + 在XQF文件中,一个棋盘位置用一个字节表示,字节值 = X * 10 + Y。因此上图 +中的黑帅可用字节值49(十进制)表示,也就是十六进制的0x31;红方的帅可用字节值 +40表示,也就是十六进制的0x28。所有被吃掉的子,用位置255,即十六进制的0xFF表 +示。 + + +三、XQF文件中初始局面的表示 + + 在XQF文件中,使用连续的32个字节表示棋盘的初始局面。 这32个字节的位置顺 +序是固定的,含义如下: + 01 - 16: 依次为红方的车马相士帅士相马车炮炮兵兵兵兵兵 + 17 - 32: 依次为黑方的车马象士将士象马车炮炮卒卒卒卒卒 + 用这32个字节的含义加上前面所说的字节值和棋盘位置的对应关系,可以用连续 +32个字节的值表示一个当前的局面。例如,全局的初始局面可以用如下的32个字节表 +示(注:所有的值为十进制,红方和黑方均从右到左排列): + (80,70,60,50,40,30,20,10,00,72,12,83,63,43,23,03, + 09,19,29,39,49,59,69,79,89,17,77,06,26,46,66,86) + + +四、XQF 1.0 文件格式说明 + + 下面的部分是XQF 1.0的文件格式说明,请严格按该格式生成XQF文件。下面说明 +中文件的偏移量用16进制表示。 XQF文件由文件头和棋谱记录两部分组成,分别说明 +如下: + +A. 文件头部 (0000 - 03FF) +-------------------------------------------------------------------------- + 0000 - 0001 XQF文件标记,必须为 'XQ' (0000 = 'X', 0001 = 'Q') + + 0002 XQF文件版本号,必须为0x0A = (XQF 1.0)。 + + 0003 - 000F 保留:必须全部为0x00 + + 0010 - 002F 这32个字节是棋局的开局局面,局面说明见“局面表示” + + 0030 - 0032 保留:必须全部为0x00 + + 0033 棋局的结果: 0x00-未知,0x01-红胜,0x02-黑胜,0x03-和棋 + + 0034 - 003F 保留:必须全部为0x00 + + 0040 棋局的类型: 0x00-全局文件, 0x01-布局文件, + 0x02-中局文件, 0x03-残局文件 + + 0041 - 004F 保留:必须全部为0x00 + + 0050 标题的长度(即下面文字的长度,下同) + 0051 - 008F 标题文字 + ---------------------------------- + 从0050-008F实际是一个字符串的保存方法,先在第一个 + 字节保存字符串的长度,接着存字符串的内容,不足的部 + 分用0x00填充(下同)。 + + 0090 - 00CF 保留:必须全部为0x00 + + 00D0 比赛名称的长度 + 00D1 - 010F 比赛名称 + + 0110 比赛日期的长度 + 0111 - 011F 比赛日期 + + 0120 比赛地点的长度 + 0121 - 012F 比赛地点 + + 0130 红方棋手名字的长度 + 0131 - 013F 红方棋手名字 + + 0140 黑方棋手名字的长度 + 0141 - 014F 黑方棋手名字 + + 0150 用时规则的长度 + 0151 - 018F 用时规则 + + 0190 红方用时的长度 + 0191 - 019F 红方用时 + + 01A0 黑方用时的长度 + 01A1 - 01AF 黑方用时 + + 01B0 - 01CF 保留: 必须全部为0x00 + + 01D0 棋谱讲评人名字的长度 + 01D1 - 01DF 棋谱讲评人名字 + + 01E0 文件作者名字的长度 + 01E1 - 01EF 文件作者名字 + + 01F0 - 03FF 保留: 全部为0x00 + +B. 棋谱记录 (0400 - 文件尾部) +-------------------------------------------------------------------------- + 从0x0400开始存放棋谱记录,每步记录的存放格式为: 8个棋谱记录字节 + 0个或 +多个字节的棋谱注解文本。其中,8个棋谱记录字节的格式为: + + 第 1 字节: 本步棋的开始位置坐标的字节值(X*10+Y) + 24 (十进制)。 + 第 2 字节: 本步棋的到达位置坐标的字节值(X*10+Y) + 32 (十进制)。 + 第 3 字节: 如果不是最后一步棋,为0xF0;如果是最后一步棋,为0x00。 + 第 4 字节: 保留: 必须为0x00。 + 第5-8字节: 为一个32位整数(x86格式,高字节在后),表明本步注解的大小, + 如果没有注解,则为0x00000000。 + 如果本步没有评注,即5-8字节全部为0x00,则本步结束,否则,随后存放本 + 步评注文本,文本不以'\0'结束(由于前面已经有大小, 该文本的长度必须等 + 于5-8字节处的整数值)。如此反复,可将所有棋步存入。(暂时不支持变着的 + 保存)。从上面可以看出如果没有注解,一步棋的记录共占8个字节。 + + 需要说明的是,在真正的对局记录开始前,必须有一步空的着法,称为第0步,该 +步的第1 - 8字节必须是 0x18, 0x20, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00。由于 +这些字节是固定的,所以真正的棋谱记录是从0x0408处开始的。如果您的棋局只有初 +始局面而没有任何着法记录(即只有一个残局局势),则文件中棋谱记录只有第0步的记 +录,并且该记录的8个字节为:0x18,0x20,0x00,0xFF,0x00,0x00,0x00,0x00。 详细的 +格式请参见本文后面的文件例子。 + + +四、给程序员 +-------------------------------------------------------------------------- + 使用上面的格式,可以将全局或残局的主变着存入XQF文件, 也可以编写XQF文件 +信息的处理程序,XQF文件信息在各版本中是相同的,并且没有任何加密措施。 + + 该文件格式的最新版本可到作者的主页(http://www.qipaile.net)上下载,如有不 +明白的地方,可以向作者询问,欢迎大家将文件保存为XQF文件。 + + 过河象(xqstudio@qipaile.net) + http://www.qipaile.net + + 2001年4月16日 + +************************************************************************** +* * +* 最新文件格式的版本请看“http://www.qipaile.net/xqstudio/XqfFormat.txt”* +* 以上URL请注意字母的大小写。 * +* * +************************************************************************** + +========================================================================== +附: XQF 1.0 格式文件的例子(为节省篇幅,只有8个回合的记录) +.......................................................................... +A. 棋谱文件内容如下: + ----------------------------------------------------------------------- + 标题: 仙人指路对起马局 + 赛事: "中立杯"象棋电视快棋赛 + 日期: 1997年11月16日 + 地点: 北京 + 红方: 柳大华 + 黑方: 吕 钦 + 结果: 黑方胜 + 评论: 刘殿中 + 作者: 过河象 + ---------------------------------------------------- + 1. 兵七进一 马8进7 2. 兵三进一 炮2平3 + 3. 相七进五 马2进1 4. 马八进七 车1平2 + 5. 车九平八 车2进4 6. 炮八平九 车2平8 + 7. 炮二进五 炮3平8 8. 马二进三 车9平8 + ---------------------------------------------------- + 棋谱由XQStudio生成 (http://www.qipaile.net/xqstudio) + +B. 对应上面棋局的XQF文件内容如下: + ---------------------------------------------------------------------- + 请参照前面的说明进行分析。 + + 0000: 58 51 0A 00 00 00 00 00-00 00 00 00 00 00 00 00 + ----- -- + 'XQ' . - 这三个字节是重要的文件标记,不可改变。 + + 0010: 50 46 3C 32 28 1E 14 0A-00 48 0C 53 3F 2B 17 03 + 0020: 09 13 1D 27 31 3B 45 4F-59 11 4D 06 1A 2E 42 56 + ----------------------------------------------- + 以上32个字节为棋局的初始局面的位置,如过您要生 + 成的文件是全局文件,可直接使用上面的数据。 + + 0030: 00 00 00 02 00 00 00 00-00 00 00 00 00 00 00 00 + -- + 棋局的结果,0x02=黑胜 + + 0040: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + -- + 棋局文件的类型: 0x00=全局文件 + + 0050: 10 CF C9 C8 CB D6 B8 C2-B7 B6 D4 C6 F0 C2 ED BE + 0060: D6 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + 0070: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + 0080: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + ----------------------------------------------- + 以上64个字节为文件标题,第一个字节为长度(0x10), + “仙人指路对起马局”共16个字节,所以长度为0x10。 + 下面所有的字符串规律相同。 + + 0090: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + 00A0: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + 00B0: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + 00C0: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + ----------------------------------------------- + 以上64个字节为保留字节,必须为0x00。 + + 00D0: 16 22 D6 D0 C1 A2 B1 AD-22 CF F3 C6 E5 B5 E7 CA + 00E0: D3 BF EC C6 E5 C8 FC 00-00 00 00 00 00 00 00 00 + 00F0: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + 0100: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + ----------------------------------------------- + 以上64个字节为比赛名称“"中立杯"象棋电视快棋赛” + + 0110: 0E 31 39 39 37 C4 EA 31-31 D4 C2 31 36 C8 D5 00 + ----------------------------------------------- + 以上16个字节为比赛日期“1997年11月16日” + + 0120: 04 B1 B1 BE A9 00 00 00-00 00 00 00 00 00 00 00 + ----------------------------------------------- + 以上16个字节为比赛地点“北京” + + 0130: 06 C1 F8 B4 F3 BB AA 00-00 00 00 00 00 00 00 00 + ----------------------------------------------- + 以上16个字节为红方棋手“柳大华” + + 0140: 06 C2 C0 20 20 C7 D5 00-00 00 00 00 00 00 00 00 + ----------------------------------------------- + 以上16个字节为黑方棋手“吕 钦” + + 0150: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + 0160: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + 0170: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + 0180: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + ----------------------------------------------- + 以上64个字节为用时规则,空,没有输入 + + 0190: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + ----------------------------------------------- + 以上16个字节为红方用时,空,没有输入 + + 01A0: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + ----------------------------------------------- + 以上16个字节为红方用时,空,没有输入 + + 01B0: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + 01C0: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + ----------------------------------------------- + 以上64个字节为保留字节,必须为0x00 + + 01D0: 06 C1 F5 B5 EE D6 D0 00-00 00 00 00 00 00 00 00 + ----------------------------------------------- + 以上16个字节为棋谱讲评“刘殿中” + + 01E0: 06 B9 FD BA D3 CF F3 00-00 00 00 00 00 00 00 00 + ----------------------------------------------- + 以上16个字节为文件作者“过河象” + + 01F0: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + ... 为节省篇幅,省去许多0x00 ... + 03F0: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 + ----------------------------------------------- + 以上从0x1F0-0x3F0的所有字节为保留字节,为0x00 + + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 从下面(0x400)出开始为棋谱记录 + 0400: 18 20 F0 FF 00 00 00 00-2F 38 F0 00 00 00 00 00 + ----------------------- ----------------------- + 第0步,内容固定(8字节) 第1步: 兵七进一 + ----------------------- + + 第1步“兵七进一”内容分析如下: + ----------------------------------------------- + 兵七进一的起点坐标(2, 3), 落点坐标(2, 4),则 + 1. 起点字节值=(X*10+Y)+24 = 23+24 = 47 (0x2F) + 2. 落点字节值=(X*10+Y)+32 = 24+32 = 56 (0x38) + 3. 本步不是最后一步,所以第3个字节为 (0xF0) + 4. 第4个字节是保留字节,必须为0x00 (0x00) + 5. 本步没有注解,所以5-8字节为0x00. (0x00) + 6. (0x00) + 7. (0x00) + 8. (0x00) + + 如果棋谱记录中没有任何着法,则只有第0步的棋谱记 + 录,并且第0步棋谱记录的第三个字节为0x00, 文件内 + 容到0x0407处就结束了。 + + 0410: 67 63 F0 00 00 00 00 00-57 60 F0 00 00 00 00 00 + ----------------------- ----------------------- + 马8进7 兵三进一 + + 0420: 29 3B F0 00 00 00 00 00-2C 4A F0 00 00 00 00 00 + ----------------------- ----------------------- + 炮2平3 相七进五 + + 0430: 2B 27 F0 00 00 00 00 00-22 36 F0 00 00 00 00 00 + ----------------------- ----------------------- + 马2进1 马八进七 + + 0440: 21 33 F0 00 00 00 00 00-18 2A F0 00 00 00 00 00 + ----------------------- ----------------------- + 车1平2 车九平八 + + 0450: 2B 2F F0 00 00 00 00 00-24 22 F0 00 00 00 00 00 + ----------------------- ----------------------- + 车2进4 炮八平九 + + 0460: 27 6B F0 00 00 00 00 00-60 6D F0 00 00 00 00 00 + ----------------------- ----------------------- + 车2平8 炮二进五 + + 0470: 33 6D F0 00 00 00 00 00-5E 5E F0 00 00 00 00 00 + ----------------------- ----------------------- + 炮3平8 马二进三 + + 0480: 71 6F 00 00 00 00 00 00 + ----------------------- + 车9平8 (注意第3个字节,最后一步, 应为0x00) + +注:1. 如果将上面的全部数据存到一个文件中,即形成一个标准的XQF文件,该文件 + 的大小为0x0488(十进制1160)个字节。 + 2. 为研究和测试XQF 1.0文件结构,可下载XQStudio 1.0的版本生成多个不同的 + 文件进行比较和分析,下载地址: + “http://www.qipaile.net/xqstudio/XQStudio_1_0.zip” (注意大小写) + +========================================================================== + (☆☆☆ 欢迎访问“http://www.qipaile.net” ☆☆☆) \ No newline at end of file diff --git a/XQFTOOLS/XQFEXAMA.XQF b/XQFTOOLS/XQFEXAMA.XQF new file mode 100644 index 0000000..9ab9d43 Binary files /dev/null and b/XQFTOOLS/XQFEXAMA.XQF differ diff --git a/XQFTOOLS/XQFEXAMB.XQF b/XQFTOOLS/XQFEXAMB.XQF new file mode 100644 index 0000000..287a396 Binary files /dev/null and b/XQFTOOLS/XQFEXAMB.XQF differ diff --git a/XQFTOOLS/XQFTOOLS.ISS b/XQFTOOLS/XQFTOOLS.ISS new file mode 100644 index 0000000..3aa92e8 --- /dev/null +++ b/XQFTOOLS/XQFTOOLS.ISS @@ -0,0 +1,63 @@ +; Script generated by the Inno Setup Script Wizard. +; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! + +[CustomMessages] +Quot=" + +[Setup] +AppName=XQFTools +AppVerName=XQF Tools 2.02 +AppPublisher=www.elephantbase.net +AppPublisherURL=http://www.elephantbase.net/ +AppSupportURL=http://www.elephantbase.net/ +AppUpdatesURL=http://www.elephantbase.net/ +DefaultDirName={pf}\XQFTools +DefaultGroupName=XQF Tools +LicenseFile=..\LGPL.TXT +OutputBaseFilename=xqftools +OutputDir=. +Compression=lzma +SolidCompression=yes + +[Types] +Name: "default"; Description: "Default Installation" +Name: "custom"; Description: "Custom Installation"; Flags: iscustom + +[Components] +Name: "xqf2pgn"; Description: "XQF->PGN Convertor"; Types: default +Name: "pgn2xqf"; Description: "PGN->XQF Convertor" +Name: "xqstudio"; Description: "XQF Reader - XQStudio 1.63" + +[Files] +Source: "ECCO.DLL"; DestDir: "{app}"; Flags: ignoreversion; Components: xqf2pgn +Source: "..\BIN\XQF2PGN.EXE"; DestDir: "{app}"; Flags: ignoreversion; Components: xqf2pgn +Source: "..\BIN\PGN2XQF.EXE"; DestDir: "{app}"; Flags: ignoreversion; Components: pgn2xqf +Source: "README.TXT"; DestDir: "{app}"; Flags: ignoreversion; Components: xqf2pgn or pgn2xqf +Source: "XQF.TXT"; DestDir: "{app}"; Flags: ignoreversion; Components: xqf2pgn or pgn2xqf +Source: "SAMPLE.XQF"; DestDir: "{app}"; Flags: ignoreversion; Components: xqf2pgn or pgn2xqf +Source: "SAMPLE.PGN"; DestDir: "{app}"; Flags: ignoreversion; Components: xqf2pgn or pgn2xqf +Source: "..\LGPL.TXT"; DestDir: "{app}"; Flags: ignoreversion; Components: xqf2pgn or pgn2xqf +Source: "XQSTUDIO.EXE"; DestDir: "{app}"; Flags: ignoreversion; Components: xqstudio +Source: "XQSTUDIO.TXT"; DestDir: "{app}"; Flags: ignoreversion; Components: xqstudio +Source: "XQBITMAP.ZIP"; DestDir: "{app}"; Flags: ignoreversion; Components: xqstudio +Source: "XQFEXAMA.XQF"; DestDir: "{app}"; Flags: ignoreversion; Components: xqstudio +Source: "XQFEXAMB.XQF"; DestDir: "{app}"; Flags: ignoreversion; Components: xqstudio +Source: "..\ELEBASE.URL"; DestDir: "{app}"; Flags: ignoreversion; Components: xqf2pgn or pgn2xqf or xqstudio +; NOTE: Don't use "Flags: ignoreversion" on any shared system files + +[Registry] +Root: HKCR; Subkey: ".pgn"; ValueType: string; ValueData: "XQWizard.PGN"; Components: pgn2xqf +Root: HKCR; Subkey: "XQWizard.PGN\Shell\PGN2XQF"; ValueType: string; ValueData: "Open with &XQF Reader"; Components: pgn2xqf +Root: HKCR; Subkey: "XQWizard.PGN\Shell\PGN2XQF\command"; ValueType: string; ValueData: "{app}\PGN2XQF.EXE %1"; Components: pgn2xqf +Root: HKCR; Subkey: ".xqf"; ValueType: string; ValueData: "XQStudio"; Components: xqf2pgn or xqstudio +Root: HKCR; Subkey: "XQStudio"; ValueType: string; ValueData: "XQStudio File"; Components: xqf2pgn or xqstudio +Root: HKCR; Subkey: "XQStudio\DefaultIcon"; ValueType: string; ValueData: "{app}\XQSTUDIO.EXE,0"; Components: xqstudio +Root: HKCR; Subkey: "XQStudio\Shell\Open"; ValueType: string; ValueData: "&Open"; Components: xqstudio +Root: HKCR; Subkey: "XQStudio\Shell\Open\command"; ValueType: string; ValueData: "{cm:Quot}{app}\XQSTUDIO.EXE{cm:Quot} {cm:Quot}%1{cm:Quot}"; Components: xqstudio +Root: HKCR; Subkey: "XQStudio\Shell\XQF2PGN"; ValueType: string; ValueData: "Open with &PGN Reader"; Components: xqf2pgn +Root: HKCR; Subkey: "XQStudio\Shell\XQF2PGN\command"; ValueType: string; ValueData: "{app}\XQF2PGN.EXE %1"; Components: xqf2pgn + +[Icons] +Name: "{group}\XQF2PGN & PGN2XQF Readme"; Filename: "{app}\README.TXT"; Components: xqf2pgn or pgn2xqf +Name: "{group}\XQStudio"; Filename: "{app}\XQSTUDIO.EXE"; Components: xqstudio +Name: "{group}\www.elephantbase.net"; Filename: "{app}\ELEBASE.URL"; Components: xqf2pgn or pgn2xqf or xqstudio diff --git a/XQFTOOLS/XQSTUDIO.EXE b/XQFTOOLS/XQSTUDIO.EXE new file mode 100644 index 0000000..332ac49 Binary files /dev/null and b/XQFTOOLS/XQSTUDIO.EXE differ diff --git a/XQFTOOLS/XQSTUDIO.TXT b/XQFTOOLS/XQSTUDIO.TXT new file mode 100644 index 0000000..dde8bb9 --- /dev/null +++ b/XQFTOOLS/XQSTUDIO.TXT @@ -0,0 +1,562 @@ + “象棋演播室”(XQStudio)软件使用说明 + + XQStudio 1.63 (正式版) + + (2007-01-30) + + XQStudio是一个关于中国象棋(现在叫做“象棋”)的软件。广大 +棋迷朋友和网友们可以使用XQStudio对象棋的布局、对局和残局进行 +记录、欣赏、研究、创作和交流。XQStudio的主要特点是支持注解和 +变着,同时提供强大的棋局搜索和棋谱管理功能。现在发行的棋书和 +报刊杂志上的对局评注基本上都可以方便地进入XQStudio。XQStudio +完全可以满足各层次棋手、裁判员及象棋文字工作者的各种需要。 + +一、XQStudio的发行和版本 + + XQStudio是一个自由软件,您个人可以自由地使用它而不需要任 +何费用(一些商业厂家使用XQStudio生产的棋谱文件不包括在内)。但 +是,如果您要把XQStudio用于任何商业目的,都必须事先征得作者的 +书面许可。 + XQStudio主要通过Internet发行,最新的版本可以使用浏览器从 +“http://www.qipaile.net/xqstudio”这个站点上下载(请注意字母 +大小写, 全部为小写字母) 。您也可以自由地把它上载到任何一个共 +享软件下载站点(不必征得作者的许可)。XQStudio的版本号有类似下 +面的格式: + XQStudio 1.0 (Beta3) + 上面版本号中的'XQStudio'是软件的名称(请注意大小写)'1.0' +是软件的版本号,括号中的文字'Beta3'说明是测试版, 正在征求大 +家的意见。经过若干个测试版,系统基本定型后,最后一个测试版会 +被命名为“Final Beta”,也就是最后测试版。经过最后测试版本的 +考验,系统稳定以后,将推出系统的正式版。正式版的版本号中会去 +掉括号中的内容,即'XQStudio 1.0'。以后新的XQStudio版本号会变 +为'XQStudio 1.1 (Beta1)',依次类推。 + 从XQStudio第一个版本,也就是XQStudio 1.0 (Beta1), 1998年 +11月15日在北京首先发行到现在,已经经过了Beta1,Beta2和Beta3三 +个测试版。在各位棋迷朋友的关心和帮助下,于1999年01月13日发行 +XQStudio 1.0的最后测试版即“XQStudio 1.0 (Final Beta)”。 经 +过近一个月的测试,排除了一些程序错误,又加进了一些新的功能, +于02月12日正式推出XQStudio的第一个正式版----XQStudio 1.0。 + 目前,最高版本为2007年1月30日发布的“XQStudio 1.63”, 棋 +迷朋友可以查看本文后面部分“象棋演播室的版本历史”来了解新版 +本的主要功能的说明和新增功能的使用方法。 + + +二、XQStudio的安装和启动 + + 您将XQStudio 1.6版的压缩文件从附近Internet的站点下载回来 +以后 (文件名为XQStudio_1_6.zip), 使用WinZip将该文件解压缩到 +XQStudio(或其它)目录中。文件解开以后,执行文件'XQStudio.EXE' +是XQStudio的主程序文件,双击该文件便可以启动XQStudio。为了方 +便使用,您可以在桌面上建立XQStudio的快捷方式,XQStudio的图标 +是一个隶书的“象”字,双击该图标可以启动XQStudio。 + 通常,在压缩文件中还会包括一些已经做好的“.XQF”文件,可 +以用XQStudio打开它们来了解软件的功能。同时建议您先阅读帮助中 +的“使用说明”(在XQStudio的主选单中选择“帮助│使用说明”), +这里面含有最新版本的说明信息和本软件的使用方法。如果您对棋谱 +文件感兴趣,可以访问“http://www.qipaile.net/xqf”,上面有数 +以万计的棋谱文件供您下载! + + +三、XQStudio的运行环境: + + 操作系统: Windows 9x 或 Windows NT 4.0 以上版本,支持 + Windows ME, Windows 2000 和 Windows XP + (要求有中文系统 GB码) + 硬 件: 800 x 600 256色以上的显示分辩率 + (最好使用正常字体,大字体方式效果稍差) + + +四、使用XQStudio的一些基本知识 + + 1.XQStudio主要提供对.XQF文件的支持。.XQF文件是XQStudio产 +生的数据文件。在文件中,可以存入行棋记录、对局评注和变着等信 +息。由于XQStudio在第一步棋就可以输入变着,所以一个.XQF文件不 +仅可以存入一盘棋的对局记录,也可以将整本布局教材存入。 + + 2.走棋或粘贴棋谱记录只能在当前棋谱记录的最后进行(在棋谱 +记录的中间只能插入变着),如果不小心走错了,可以按删除按钮删 +掉最后一步棋。 + + 3.变着总是针对棋谱记录中的某一记录进行,因此要加入变着, +可以先在棋谱记录中找到指定的位置,然后按加入变着按钮。 + + 4.每一步棋都可以有注解,一般在第0步(棋谱记录为'=======') +输入一些说明信息,这样便于查询。 + + 5.XQStudio提供指点帮助功能,一般情况下,用鼠标左键指点屏 +幕上的对象,在窗口的底部会给出相应的功能说明和使用方法。初学 +者可使用该信息快速掌握XQStudio软件的使用。 + + 6.XQStudio的新功能的用法一般以版本历史的方式告诉大家,具 +体的用法可参照本文后面的“版本历史”。 + + +五、作者的联系地址: + + 作者:董世伟(过河象),E-mail: xqstudio@qipaile.net + + *** 欢迎访问: http://www.qipaile.net/xqstudio *** + 网上棋牌乐(http://www.qipaile.net) + + 欢迎朋友们通过电子邮件提出宝贵意见,同时希望广大棋迷朋友 +互相交流.XQF文件(http://www.qipaile.net/xqf上有大量的XQF棋谱 +文件)。感谢所有支持和关心XQStudio的棋迷朋友们! + + 2002年06月12日 + +============================================================== + + 象棋演播室(XQStudio)的版本历史 + + (含新功能介绍和使用方法) + + +1. XQStudio 1.0 (Beta1) 1998年11月15日 (北京) +-------------------------------------------------------------- + 经过三周日夜奋战,终于完成了XQStudio的第一个版本。虽然 + 是第一版,但是已经支持注解和变着,并且可以将棋谱、注解存入 + .XQF文件。从此,棋迷朋友们打谱就不必使用棋子了! + + +2. XQStudio 1.0 (Beta2) 1998年12月01日 (北京) +-------------------------------------------------------------- + a) 功能没有新的变化。 + b) 只是修改了在大字体模式下显示不正常的问题。 + + +3. XQStudio 1.0 (Beta3) 1998年12月17日 (北京) +-------------------------------------------------------------- + a) 界面稍微做了些调整。 + b) 增加了棋子摆放功能,可以输入中局、残局和排局。 + c) 可以输入一些棋局信息,如标题、比赛名称、地点、时间、棋手 + 姓名、棋谱评论和作者姓名等。 + + +4. XQStudio 1.0 (Final Beta) 1999年01月13日 (北京) +-------------------------------------------------------------- + a) 解决上个版本中“保存文件后标题丢失”问题。 + b) 增加了“文件关联打开”功能,通过操作系统将.XQF的打开方式 + 设置为XQStudio即可以在文件浏览器中双击“.XQF”文件名而打 + 开XQStudio。 + c) 增加了“一次打开多个文件”功能。在XQStudio中,选择文件打 + 开功能弹出“打开文件”对话框后, 可以使用Shift、Ctrl和鼠 + 标相结合的方式一次选择打开多个“.XQF”文件。在“窗口”菜 + 单选项中列出了所有打开文件的名字。 + d) 增加了“文件属性”功能。在“文件”菜单项中选择“属性”可 + 查看和指定.XQF文件的属性。这样,可以修改文件的标题和棋局 + 的相关信息。 + e) 增加了“输出文本文件”的功能。单击棋盘屏幕右边的“文本” + 标签,即可显示棋谱记录列表中列出的棋谱记录和相应的注解。 + 你可以将该文本复制到剪贴板上,也可以将它存为文本文件。需 + 要说明的是,目前文本文件尚不支持变着。 + +5. XQStudio 1.0 1999年02月12日 (北京) +-------------------------------------------------------------- + a) 解决上个版本中“同时打开文件个数太多”引起的系统问题。 + b) 调整了红方棋子的颜色,使之更加柔和好看。 + c) 增加了“自动播放”功能。在XQStudio中,用鼠标右键单击棋谱 + 列表,可以显示自动播放的弹出菜单。在菜单中可以设置时间间 + 隔和声音提示。另外,也可以用鼠标双击棋谱记录的某一步开始 + 自动播放,单击棋谱记录停止自动播放。 + d) 将“变着显示窗口”改变为“所有走法显示窗口”,这样某一步 + 的所有走法都显示在列表中,同时指示了当前的走法。每个走法 + 有一个固定的序号,不会乱次序。同时,增加了调整变着次序的 + 的按钮,可以修改变着的先后次序。请注意,不能删除当前显示 + 的变着。 + +6. XQStudio 1.1 1999年06月06日 (北京) +-------------------------------------------------------------- + a) 增加了“一次自动播放多个棋谱文件”的功能。在XQStudio的菜 + 单中,选择“文件|自动播放棋谱文件...”,可打开自动播放设 + 置窗口,按提示可实现该功能。 + b) 增加了“文本棋图输出”功能。在任何一步的局势下,单击窗口 + 右边的“棋图”页签,可显示该局势的文本图。可将该图复制到 + 剪贴板或文本文件中,方便了在公告板上发布棋图。 + c) 增加了“残局图形棋图输出”功能。在XQStudio中,新建残局文 + 件窗口中,棋子摆放好后,用鼠标右键单击摆放棋子的小棋盘, + 可弹出棋图操作的功能菜单。 + d) 修改了.XQF文件的一些结构,使之文件更小,更适合在Internet + 上交流。同时该版本还兼容以前版本的文件格式,所有以前版本 + 产生的文件本版本都可以读,反之不可。 + e) 对一些界面做了一些调整。 + +7. XQStudio 1.2 1999年07月06日 (北京) +-------------------------------------------------------------- + a) 增加了“自动从剪贴板粘贴棋谱记录”的功能。在象棋论坛上或 + 其它文本编辑器中,将棋谱记录文本选中后复制到剪贴板上,然 + 后在XQStudio中新建一个文件,用鼠标右键单击棋谱列表(棋谱 + 列表是指显示走棋记录的地方,如“1. 炮二平五 马八进七”) + 弹出选单中选择“从剪贴板粘贴棋谱”,可将棋谱记录自动加入 + 棋谱列表,而不需要人工一步一步输入,方便了棋谱的录入。本 + 功能只能在当前棋局的最后进行,一次可粘贴一步或多步棋谱记 + 录。可以粘贴多种格式的棋谱记录,如“炮二平五”可表示为: + 传统中文格式: 炮二平五 + AXF 格式: C2=5 + WXF 格式: C2.5 + ICCS 格式: H2-E2 + 这样,大家看到的一些英文棋谱记录,也可以同样处理了。如果 + 棋谱文本是由XQStudio自动产生的,粘贴时还可以自动将标题、 + 赛事、时间、红方、黑方、评论、作者、结果等信息自动录入。 + 需要注意的是,目前XQStudio粘贴棋谱记录时只能处理由本软件 + 自动生成的文本文件中的注解。对于其它格式的文本文件,粘贴 + 棋谱时,如果注解中有棋谱的着法,需要先将这些着法删掉,以 + 免影响棋谱的粘贴。 + b) 在工具条上增加了“棋盘倒置”按钮,交换红黑方更加容易。 + c) 将文本输出“页”上移到“棋图”标签的旁边,同时支持单栏、 + 双栏和三栏格式的文本输出,便于排版和打印。 + d) 增加了“自动从剪贴板粘贴棋子位置”的功能。在象棋论坛上或 + 其它文本编辑器中,将和本软件文本棋图(大棋图格式)格式兼容 + 的文本选中后复制到剪贴板上,然后在XQStudio中新建一个中局 + 或残局文件,在新建向导第二页摆放棋子位置的小棋盘上,单击 + 鼠标的右键,弹出选单中选择“从剪贴板文本棋图中粘贴棋子位 + 置”,可将棋子位置自动摆放到小棋盘上,方便了残局的位置摆 + 放。借助于本软件的文本棋图输出功能和该功能,您可以方便地 + 将.XQF文件中的任何一步局势作成中局或残局文件,也可以更加 + 容易地向论坛贴棋图和阅读棋图。免去了摆放棋子位置的麻烦, + 解答残局将变成一件乐事。 + +8. XQStudio 1.3 2001年02月25日 (北京) +-------------------------------------------------------------- + a) 修改和增加了“自动打开文件”的功能(也就是Windows系统文件 + 关联功能)。具体的设置可用下面步骤: + 1) 如果你还没有建立XQF文件关联或不知道什么是文件关联, + 请直接转第11步; + 2) 如果你建立了XQF关联但不能正常工作,请继续下一步; + 3) 用鼠标右键单击“开始”按钮,打开“资源管理器”; + 4) 在“资源管理器”中选择“查看|文件夹选项”菜单; + 5) 这时显示出“文件夹选项”对话框,继续下一步; + 6) 单击“文件类型”标签; + 7) 在“已注册的文件类型”中找到XQF文件类型并用选中; + 8) 单击“删除”按钮将该文件类型删除; + 9) 在确认删除对话框中选择“是”,将该文件类型删除; + 10) 关闭“文件夹选项”对话框,继续下一步; + 11) 用资源管理器找到任一个XQF文件; + 12) 鼠标双击该文件名,这时弹出“打开方式”对话框; + 13) 在“打开方式”对话框中单击“其它”按钮; + 14) 在弹出的对话框中寻找“XQStudio.EXE”文件; + 15) 找到该文件后单击“打开”; + 16) 这时“XQStudio”会出现在打开方式窗口的“选择要使用 + 的程序”列表中; + 17) 在列表中单击“XQStudio”使之变兰,也就是选中; + 18) 单击“确定”按钮即可以打开XQStudio; + 19) 此时.XQF文件已经和XQStudio关联。也就时说你在资源管 + 理器中通过用鼠标双击.XQF文件名来用XQStudio打开该文 + 件。此打开方式同样可用于打开ZIP文件中的XQF文件,你 + 可以不解压缩ZIP文件即可以看棋谱。 + 以上步骤在Windows 98环境中试验无问题,请大家试验一下。 + b) 增加了新的走子方式,点击走子和拖动走子均可。 + c) 增加了“左右调换”的功能。 + d) 在文件属性中增加了“用时规则”和“红、黑方用时”信息。 + e) 增加了“帅”和“将”的安全检测,如果走棋后盘面出现将帅直 + 接对面或对方棋子下一步可吃掉自己的“帅”或“将”,系统将 + 自动取消本步棋。 + f) 在原有粘贴棋谱的功能的基础上,进一步增强了对纯数字坐标棋 + 谱(如32-62)的支持。 这样就可以粘贴一些象棋论坛上的Java棋 + 谱了。一般的操作方式是在浏览器中将看到的Java棋图的网页切 + 换到原代码的方式,选中棋谱部分,复制到剪贴板,然后采用粘 + 贴棋谱的方式。 + g) 增加了不通过剪贴板,直接转入其它非.XQF格式的文件,如:联 + 众的的中国象棋存盘文件(.chn)和棋谱文本文件(.txt)。具体的 + 方法如下: + 在XQStudio中新建一个空的文件,用鼠标右键单击棋盘旁边显 + 示文字棋谱记录的棋谱列表,这时弹出功能选单,在弹出选单 + 中选择“从非XQF文件中转入棋谱...”,这时出现打开文件对 + 话框,选择指定的文件,单击“打开”可将文件中的棋谱记录 + 自动加入棋谱列表。 + +9. XQStudio 1.5 2001年03月25日 (北京) +-------------------------------------------------------------- + a) 增加了“XQF文件查找和浏览功能”, 可以按文件名和文件内容 + 查找指定的文件,同时可以非常方便地查看文件中棋子在任一步 + 的位置,极大地解决了浏览布局或中残局的困难。该功能的用法 + 如下: + 1) 选择主选单“文件|查找浏览XQF文件”或直接按工具条上 + 的小图标,打开文件查找窗口。 + 2) 输入查询条件,也可以不输入(表示全部); + 3) 按“浏览”按钮选择一个文件夹; + 4) 按“开始查找”按钮开始查找; + 5) 查找的结果在下面的列表中,你可以用鼠标单击查看该文 + 件的信息,双击直接打开该文件; + 6) 如果需要,调整左边窗口下面的设置,可以查看不同的局 + 面。 + 如果希望把“查找”界面,换成象资源管理器的界面,只接按屏 + 幕右上方的“浏览文件”单选按钮就可以了。这样就可以象翻书 + 一样看文件了,多方便! + b) 增加了“局面查询”的功能,也就是说,给定任一个局面,指定 + 一个查找范围,即可将所有含有这个局面的棋谱全部找出。“局 + 面查找”不仅可以查找棋谱的初始位置,更可以深入棋局内部。 + 如果你手边有较多的.XQF文件,那么要想知道某个局面都有谁下 + 过,简直是易如反掌。“局面查询”是棋界专业人士分析对手和 + 研究象棋的有力工具。要查寻的局面可以通过下面方法获得: + 1) 手工摆放; + 2) 粘贴(在棋盘上点击鼠标右键); + 3) 浏览棋谱文件的某一步,得到棋图; + 4) 打谱时用鼠标右键单击棋谱列表,选择“导出棋子位置到 + 文件查找窗口”得到棋图(即见即查)。 + c) 查找或浏览出的任一个局面,不用打开文件就可以通过左边的小 + 棋盘进行走棋研究。 + d) 改进了棋图输出的格式和操作程序,在打谱时,任一步不需通过 + 棋图文本剪贴板就可以导出棋图(用法:在棋谱列表上点击鼠标 + 右键)。导出的棋图,是和报刊杂志上风格一样的黑白图,通过 + 将棋图粘贴到Word文档中,可以作出漂亮的稿件。单击“棋图” + 标签中的“报刊、书籍排版图”标签,可以预览黑白图效果。 + e) 可以导入繁体字的文本棋谱,用来转换港台地区网页上的文本棋 + 谱,使用方法同粘贴棋谱。 + f) 支持从流行的Java棋盘中导入棋谱。现在,有些象棋站点提供了 + 用Java小棋盘演示对局的功能。这时,如果你想把这些棋谱自动 + 转入XQStudio,可以使用浏览器查看源文件的功能,以IE为例, + 步骤如下: + 1) 假定你上网时用流览器看到了一个Java棋盘可以演示对局 + 记录(可在www.cchess.net或某些象棋论坛上找个例子); + 2) 单击IE选单“查看|源文件”; + 3) 这时在记事本(NOTEPAD)中显示HTML文件的文本格式; + 4) 将HTML全部或棋谱部分复制到剪帖板; + 5) 在XQStudio新建一个全局文件; + 6) 鼠标右键单击“棋谱列表”,使用粘贴棋谱的功能将棋谱 +    记录自动转入XQStudio。 + XQStuio 1.5 已经支持大部分的Java棋谱了。 + g) 可以输出字符BBS上用的彩色文字棋图了。 该功能主要是为了方 + 便在大学校园常见的字符BBS上交流棋图用。这些字符BBS站点, + 经常使用ASCII颜色控制字符来表示不同的颜色,XQStudio 可以 + 自动地生成这些控制字符,您只需要将生成的结果复制到剪贴板 + 再粘贴到BBS上即可。不过值得注意的是:在粘贴由XQStudio自动 + 生成的彩色棋图时, 由于加了ASCII颜色控制字符,一行的长度 + 往往超过了75列,这样,大家在使用CTerm等BBS软件时,该软件 + 的“粘贴自动排版”功能将会对较长的行进行拐行处理,导制棋 + 图破坏。为达到好的效果,请禁止该功能。如果用的是CTerm, + “禁止粘贴自动排版”的操作方法如下: + 1) 在CTerm中选择“编辑|系统设置”选单。 + 2) 将“粘贴自动排版”功能去掉。 + 这样就没问题了。 在贴图前,最好预览一下,大部分字符BBS站 + 点的预览功能键为“Esc + C"。 + h) 可以自动记住最近查找和浏览的文件夹。 + i) 提供了“日集月累”的小窗口,显示XQStudio的基本知识和使用 + 技巧,您以在“帮助|日集月累”选单中开启或关闭此功能。 + j) 将常用的棋谱导入和导出功能加到了“工具”选单中。 + +10. XQStudio 1.5(第二版) 2001年04月11日 (北京) +-------------------------------------------------------------- + a) 改进了对繁体中文棋谱的支持,可直接粘贴Big5码的文本棋谱。 + b) 在“工具”选单和工具条上增加了“推演棋盘”按钮,这样在打 + 谱时遇到复杂的局面,可随时单击该按钮弹出一个当前棋局的棋 + 盘进行棋局推演。在棋局推演棋盘上,若过不小心走错了着法, + 可以用鼠标左键双击最后走过的棋子进行回退。重复回退功能可 + 以回到初始局面。使用“推演棋盘”,还可以不看棋局的走法测 + 试自己对残局或中局局面的理解。 + c) 在“文件”选单和工具条上增加了“新建空全局文件”按钮,可 + 快速建立一个空的全局文件。 + d) 在文件查找窗口中增加了“连环查找”的功能,可以做到在当前 + 查找的结果中再进行新的查找,这样,可以通过反复指定不同的 + 查找条件得到满意的查找结果。 + e) 增加了文件查找结果的管理功能。在文件查找窗口中的文件列表 + 上单击鼠标右键,可弹出文件查找结果管理功能选单,您可以将 + 查找到的文件“复制”或“移动”到指定的文件夹。通过组合使 + 用鼠标和Ctrl、Alt功能键盘, 可以在查找结果中选择特定的文 + 件进行管理。使用此功能,制作布局或棋手专集简直易如反掌! + f) 增强了用户设置的保存功能,可以记下自动播放时间间隔等用户 + 配置信息。 + +11. XQStudio 1.5(第三版) 2001年08月29日 (北京) +-------------------------------------------------------------- + a) 增强了对Big5码棋谱粘贴的支持。 + b) 在主选单和工具条上增加了“导出棋谱记录文本到剪贴板”的按 + 钮,方便了使用。 + b) 在“工具”选单上增加了“导出棋谱记录的"Java Applet" HTML + 代码到剪贴板”按钮,这样可以在支持HTML格式的论坛上方便地 + 发布棋谱了,Java棋盘同时支持全局和中残局。同时,使用本软 + 件的粘贴棋谱功能,可以将发布在论坛上的HTML格式的Java棋盘 + 和棋谱粘贴到XQStudio中(支持全局和中残局)。 + c) 增强了自动播放棋谱时的走子显示。 + d) 增加了“最近打开文件列表”的功能,简化了文件打开的操作。 + e) 增加了直接发送E-Mail的功能。 + f) 在“帮助”选单中增加了直接链接到作者E-Mail和主页地址的功 + 能,使得查看最新消息和提交反馈信息更加容易。 + g) 在“编辑”选单中增加了修改初始局面子力位置的功能,如果在 + 录入残局时,不小心将初始局面摆错了,可使用此功能快速修改 + 初始局面。 + h) 将“粘贴”棋谱的功能移到了“编辑”选单下,并在工具条上建 + 立了图标按钮。同时增加了“创建新文件并粘贴棋谱的功能”使 + 的粘贴棋谱的工作只需要一个按钮就可以完成。本版本还具备打 + 开时自动检测剪贴板内容,如果有棋谱则自动创建新的文件并粘 + 贴棋谱。有了此功能后,如果在网上看到好的棋谱,只需要先将 + 棋谱复制到剪贴板,打开XQStudio即可直接看到该棋谱,不需要 + 任何其它的操作。 + i) 改进了双击文件名启动XQStudio的方式,新版本只启动程序的一 + 个副本,即如果双击.XQF文件名时已经打开了XQStudio,那么不 + 再启动新的副本,直接在原有的窗口中打开新的文件。 + j) 增加了“键盘输入棋谱的功能”,采用键盘右方的“数字键”进 + 棋谱录入。棋谱录入的输入框在棋谱列表的上方。数字和棋谱的 + 对应关系为: + 1:帅将; 2:士; 3:相象; 4:车; 5:马; 6:炮; 7:兵卒 + 8:前; 9:后 + 1,4,7:进; 2,5,8:平; 3,6,9:退 + 例如,“炮二平五”的键盘输入为:6225, 6255 或 6285 + “马8进7”的键盘输入为:5817, 5847 或 5877 + “前车退一”的键盘输入为:7431, 7461 或 7491 + “卒7进1”的键盘输入为:7711, 7741 或 7771 + 注意,只能在棋谱的最后输入,只行棋记录的中部,键盘输入棋 + 谱的输入框是不可见的。如果在输入的过程中出现录入错误,棋 + 步没输入完时可按数字键盘右边'+'或Enter将输入区清空;如果 + 棋步已经录入,可按小键盘上的'.'(Del)或空格键删除刚才输入 + 的棋步。除了数字输入之外,XQStudio还为喜欢汉语拼音的棋友 + 提供了基于拼音系统的键盘输入,棋谱和键盘的对应关系为: + 帅:s; 将:j; 士:s; 相象:x; 车:c; 马:m; 炮:p; 兵b; 卒:z + 前:q; 后:h; 进:j; 退:t; + 对应的规则是取汉语拼音的第一个字母,XQStudio会自动识别字 + 母“S”的含义是“帅”还是“士”。在拼音系统中,数字部分 + 可直接对应键盘上的数字键,为输入方便,也可以采用数字键下 + 面一一对应的QWERTYUIO这九个键代替数字键,完成123456789数 + 字的输入。拼音输入法举例如下: + “炮二平五”的键盘输入为:p2p5 + “马8进7”的键盘输入为:m8j7 + “前车退一”的键盘输入为:qct1 (注意车的读音为Che) + “卒7进1”的键盘输入为:z7j1 + 实际上等于XQStudio提供了两套键盘输入法,喜欢键盘输入的朋 + 友可以根据自己的爱好,任选一套或两套并用。 + + k) 增强了“棋谱查找功能”,新提供了“在变着中查”、“只查子 + 力个数”和“忽略余子”等功能,可做到模糊查找。具体的含义 + 在文件查找窗口中将鼠标放到相应的选择项上,即可在下面提示 + 行得到查找提示信息。可灵活组合使用这些新的查找功能。 + + l) 在“编辑”选单和工具条上增加了“将其它.xqf文件中的棋谱记 + 录合并到本文件”,即“棋谱合并”功能。使用该功能,可以一 + 次将一个或多个(打开文件时用Ctrl,Shift键和鼠标组合)棋谱文 + 件合并到当前文件中。合并后的棋谱文件集合并前所有文件的变 + 化为一体,为研究布局和研究对手提供了极大的方便。为了不改 + 变合并前的文件内容,可新建一空文件,然后将所有的棋谱文件 + 合并到当前的空文件中。当然,合并的前提条件是两个文件的初 + 始局面必须相同。由于系统资源有限,不可能将无限多的文件合 + 并到一个文件中去,作者曾成功将胡荣华专集中的500个xqf文件 + 合并到一个.xqf文件中去,同时合并文件个数的上限取决于系统 + 中内存等系统资源的多少。 + + m) 采用了全新的执行文件压缩技术,在保证软件功能不减的前提下 + 使得执行文件的大小达到了不可思意的380K,极大地加快了执行 + 文件加载速度。 + + n) 修正了“变着注解不更新”的错误。 + + o) 增加了不通过剪贴板,直接转入弈天象棋存盘格式文件(*.MXQ) + 和中国游戏中心象棋存盘文件(*.CCM),使用方法同联众文件的使 + 用方法(见前面版本的说明)。同时在工具条上添加了转入棋谱文 + 件的按钮。 + + p) 增加了保存最后打开文件所在文件夹的功能,下次使用XQStudio + 打开文件时,自动转到最后使用过的文件夹。 + + +12. XQStudio 1.6(正式版) 2002年06月12日 (北京) +-------------------------------------------------------------- + a) 增加了“将非xqf文件批量转换为xqf文件”的功能。在XQStudio + 的“文件”选单中,选择“将非xqf文件批量转换为xqf文件”后 + 按屏幕提示做即可。请注意:转换后的文件和原文件在同一个目 + 录下,名字相同,扩展名为“.XQF”。为方便整理,可在Windows + 文件浏览器中将文件按扩展名排序,这样,所有的.XQF文件将排 + 在一起,以便于进一步处理。 + b) 由于批量文件转换功能支持文本文件,这样如果将其它格式的棋 + 谱文件变为XQStudio棋谱文本的标准格式,也可使用上面功能批 + 量生成XQF文件。 + +13. XQStudio 1.62(正式版) 2004年01月07日 (北京) +-------------------------------------------------------------- + a) 增加了“自定义图象棋图输出”功能。为了解决前面版本输出的 + 棋图达不到印刷质量的问题,在这个版本的XQStudio中,加入了 + 输出自定义棋图的功能。具体的工作步骤如下: + + 1) 制作自己的棋盘和棋子 + ~~~~~~~~~~~~~~~~~~~~~~~ + 用任何图象工具,制作好符合自己大小要求的棋盘和棋子 + 并保存为Windows Bitmap格式的文件(必须为该格式,后缀 + 为“.BMP”,并用Windows自带的画笔程序可打开查看)。 + + 2) 将制作好的BMP文件按约定的名字放到指定的目录 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 在C:盘的根目录建立'C:\XQBitmap'目录,将BMP文件按下 + 面的命名规则命名 + C:\XQBitmap\XQBoard.BMP - 棋盘 + C:\XQBitmap\XQRed1.BMP - 车(红) + C:\XQBitmap\XQRed2.BMP - 马(红) + C:\XQBitmap\XQRed3.BMP - 相(红) + C:\XQBitmap\XQRed4.BMP - 士(红) + C:\XQBitmap\XQRed5.BMP - 帅(红) + C:\XQBitmap\XQRed6.BMP - 炮(红) + C:\XQBitmap\XQRed7.BMP - 兵(红) + C:\XQBitmap\XQBlk1.BMP - 车(黑) + C:\XQBitmap\XQBlk2.BMP - 马(黑) + C:\XQBitmap\XQBlk3.BMP - 象(黑) + C:\XQBitmap\XQBlk4.BMP - 士(黑) + C:\XQBitmap\XQBlk5.BMP - 将(黑) + C:\XQBitmap\XQBlk6.BMP - 炮(黑) + C:\XQBitmap\XQBlk7.BMP - 卒(黑) + 棋盘和棋子的大小及颜色可任意制做,但是所有棋子的规 + 格必须一致 + + 3) 通知XQStudio自定义棋盘的大小和坐标 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 用记事本在C:\XQBitmap目录下建立名为XQXYWH.TXT文件, + 也就是“C:\XQBitmap\XQXYWH.TXT”这个文件,该文件共 + 有四行内容,每一行都是一个数字,这些数字是棋盘的大 + 小和坐标。 + 第一行: 棋盘上最左上角棋子位置放置棋子时的X坐标 + 最左边的为0,往右依次增大 + (坐标单位是象素,可用画笔程序查看,下同) + + 第二行: 棋盘上最左上角棋子位置放置棋子时的Y坐标 + 最上边的为0, 往下依次增大 + + 第三行: 棋盘上两条竖线之间的间距,单位为象素数 + + 第三行: 棋盘上两条横线之间的间距,单位为象素数 + + 例如:C:\XQBitmap\XQXYWH.TXT文件的内容如下: + 3 + 15 + 40 + 40 + 表示:最左上角停放棋子的位置(也就是开局局面的黑车 + 的位置)的横坐标X=3,纵坐标Y=15,棋盘竖线间距为40 + 个象素,横线间距也为40个象素。 + + + 4) 核对下面16个文件准确无误后,可运行XQStudio + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + C:\XQBitmap\XQBoard.BMP - 棋盘 + C:\XQBitmap\XQRed1.BMP - 车(红) + C:\XQBitmap\XQRed2.BMP - 马(红) + C:\XQBitmap\XQRed3.BMP - 相(红) + C:\XQBitmap\XQRed4.BMP - 士(红) + C:\XQBitmap\XQRed5.BMP - 帅(红) + C:\XQBitmap\XQRed6.BMP - 炮(红) + C:\XQBitmap\XQRed7.BMP - 兵(红) + C:\XQBitmap\XQBlk1.BMP - 车(黑) + C:\XQBitmap\XQBlk2.BMP - 马(黑) + C:\XQBitmap\XQBlk3.BMP - 象(黑) + C:\XQBitmap\XQBlk4.BMP - 士(黑) + C:\XQBitmap\XQBlk5.BMP - 将(黑) + C:\XQBitmap\XQBlk6.BMP - 炮(黑) + C:\XQBitmap\XQBlk7.BMP - 卒(黑) + C:\XQBitmap\XQXYWH.TXT - 棋盘坐标定位文件 + + 5) 在XQStudio中使用该功能 + ~~~~~~~~~~~~~~~~~~~~~~~~~ + 该功能的用法和生成排版图的用法相同,请使用。为方便 + 大家使用,可在下面的地址下载自定义棋盘的样本: + http://www.qipaile.net/xqstudio/XQBitmap.zip + 下载后,将文件放到C:\XQBitmap目录就可以测试了。 + +14. XQStudio 1.63(正式版) 2007年01月30日 (北京) + a) 解决了文件关联问题的BUG(在有些系统上.XQF文件无法直接关联) + b) 增加了对QQ中国象棋存盘文件(*.che)的导入的支持 + +15. 下个版本预告 +-------------------------------------------------------------- + 下一个推出的象棋演播室版本为“XQStudio 2.0”(注:从1.63 + 版直接跳到2.0版本),2.0版将在1.6版的基础上对XQStudio的功能 + 进行较大的增强,希望广大棋迷朋友多提建议,同时,请棋迷朋友 + 关注网上信息。 + + +欢迎提出宝贵意见,感谢所有支持和关心XQStudio的朋友们。 +来信请寄: xqstudio@qipaile.net + diff --git a/XQFTOOLS/ccm2pgn.cpp b/XQFTOOLS/ccm2pgn.cpp new file mode 100644 index 0000000..ff08e89 --- /dev/null +++ b/XQFTOOLS/ccm2pgn.cpp @@ -0,0 +1,137 @@ +/* +CCM->PGN Convertor - a Chinese Chess Score Convertion Program +Designed by Morning Yellow, Version: 3.14, Last Modified: Jun. 2008 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#ifdef _WIN32 + #include +#else + #include +#endif +#include "../base/base2.h" +#include "../eleeye/position.h" +#include "../cchess/cchess.h" +#include "../cchess/ecco.h" +#include "../cchess/pgnfile.h" + +static const int CCM2PGN_ERROR_OPEN = -2; +static const int CCM2PGN_ERROR_CREATE = -1; +static const int CCM2PGN_OK = 0; + +int Ccm2Pgn(const char *szCcmFile, const char *szPgnFile, const EccoApiStruct &EccoApi) { + int mv, nStatus; + bool bRead, bFlip; + PgnFileStruct pgn; + PositionStruct pos; + char cCcm[8]; + FILE *fp; + uint32_t dwEccoIndex, dwFileMove[20]; + + fp = fopen(szCcmFile, "rb"); + if (fp == NULL) { + return CCM2PGN_ERROR_OPEN; + } + + pgn.posStart.FromFen(cszStartFen); + pos = pgn.posStart; + + bRead = bFlip = false; + while (fread(&cCcm, 7, 1, fp) > 0) { + if (!bRead) { + bRead = true; + if (cCcm[5] < 5) { + bFlip = true; + } + } + if (bFlip) { + cCcm[3] = 8 - cCcm[3]; + cCcm[5] = 9 - cCcm[5]; + cCcm[4] = 8 - cCcm[4]; + cCcm[6] = 9 - cCcm[6]; + } + mv = MOVE(COORD_XY(cCcm[3] + FILE_LEFT, cCcm[5] + RANK_TOP), + COORD_XY(cCcm[4] + FILE_LEFT, cCcm[6] + RANK_TOP)); + mv &= 0xffff; // 防止TryMove时数组越界 + pgn.nMaxMove ++; + if (pgn.nMaxMove <= 20) { + dwFileMove[pgn.nMaxMove - 1] = Move2File(mv, pos); + } + // 中游可能允许把将吃掉,但ElephantEye不允许,所以跳过非法着法 + if (TryMove(pos, nStatus, mv)) { + pgn.wmvMoveTable[pgn.nMaxMove] = mv; + } else { + pgn.nMaxMove --; + } + if (pos.nMoveNum == MAX_MOVE_NUM) { + pos.SetIrrev(); + } + } + + if (pgn.nMaxMove < 20) { + dwFileMove[pgn.nMaxMove] = 0; + } + if (EccoApi.Available()) { + dwEccoIndex = EccoApi.EccoIndex((const char *) dwFileMove); + strcpy(pgn.szEcco, (const char *) &dwEccoIndex); + strcpy(pgn.szOpen, EccoApi.EccoOpening(dwEccoIndex)); + strcpy(pgn.szVar, EccoApi.EccoVariation(dwEccoIndex)); + } + + fclose(fp); + return (pgn.Write(szPgnFile) ? CCM2PGN_OK : CCM2PGN_ERROR_CREATE); +} + +#ifndef MXQFCONV_EXE + +int main(int argc, char **argv) { + EccoApiStruct EccoApi; + char szLibEccoPath[1024]; + + if (argc < 2) { + printf("=== CCM->PGN Convertor ===\n"); + printf("Usage: CCM2PGN CCM-File [PGN-File]\n"); + return 0; + } + + PreGenInit(); + ChineseInit(); + LocatePath(szLibEccoPath, cszLibEccoFile); + EccoApi.Startup(szLibEccoPath); + + switch (Ccm2Pgn(argv[1], argc == 2 ? "CCM2PGN.PGN" : argv[2], EccoApi)) { + case CCM2PGN_ERROR_OPEN: + printf("%s: File Opening Error!\n", argv[1]); + break; + case CCM2PGN_ERROR_CREATE: + printf("File Creation Error!\n"); + break; + case CCM2PGN_OK: +#ifdef _WIN32 + if (argc == 2) { + ShellExecute(NULL, NULL, "CCM2PGN.PGN", NULL, NULL, SW_SHOW); + } +#endif + break; + } + EccoApi.Shutdown(); + return 0; +} + +#endif diff --git a/XQFTOOLS/che2pgn.cpp b/XQFTOOLS/che2pgn.cpp new file mode 100644 index 0000000..1390f36 --- /dev/null +++ b/XQFTOOLS/che2pgn.cpp @@ -0,0 +1,147 @@ +/* +CHE->PGN Convertor - a Chinese Chess Score Convertion Program +Designed by Morning Yellow, Version: 3.14, Last Modified: Jun. 2008 +Copyright (C) 2004-2008 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#ifdef _WIN32 + #include +#else + #include +#endif +#include "../base/base2.h" +#include "../eleeye/position.h" +#include "../cchess/cchess.h" +#include "../cchess/ecco.h" +#include "../cchess/pgnfile.h" + +static int ReadInt(FILE *fp) { + int nResult, n; + nResult = 0; + n = fgetc(fp) - '0'; + while (n >= 0 && n <= 9) { + nResult *= 10; + nResult += n; + n = fgetc(fp) - '0'; + } + return nResult; +} + +static const int CHE2PGN_ERROR_OPEN = -2; +static const int CHE2PGN_ERROR_CREATE = -1; +static const int CHE2PGN_OK = 0; + +int Che2Pgn(const char *szCheFile, const char *szPgnFile, const EccoApiStruct &EccoApi) { + int i, nMoveNum, mv, nStatus; + int xSrc, ySrc, xDst, yDst; + PgnFileStruct pgn; + PositionStruct pos; + FILE *fp; + uint32_t dwEccoIndex, dwFileMove[20]; + + fp = fopen(szCheFile, "rb"); + if (fp == NULL) { + return CHE2PGN_ERROR_OPEN; + } + + pgn.posStart.FromFen(cszStartFen); + pos = pgn.posStart; + ReadInt(fp); + nMoveNum = ReadInt(fp); + + for (i = 0; i < nMoveNum; i ++) { + ReadInt(fp); + ReadInt(fp); + ReadInt(fp); + ySrc = 12 - ReadInt(fp); + xSrc = ReadInt(fp) + 3; + yDst = 12 - ReadInt(fp); + xDst = ReadInt(fp) + 3; + ReadInt(fp); + ReadInt(fp); + ReadInt(fp); + + mv = MOVE(COORD_XY(xSrc, ySrc), COORD_XY(xDst, yDst)); + mv &= 0xffff; // 防止TryMove时数组越界 + pgn.nMaxMove ++; + if (pgn.nMaxMove <= 20) { + dwFileMove[pgn.nMaxMove - 1] = Move2File(mv, pos); + } + // QQ象棋允许把将吃掉,但ElephantEye不允许,所以跳过非法着法 + if (TryMove(pos, nStatus, mv)) { + pgn.wmvMoveTable[pgn.nMaxMove] = mv; + } else { + pgn.nMaxMove --; + } + if (pos.nMoveNum == MAX_MOVE_NUM) { + pos.SetIrrev(); + } + } + + if (pgn.nMaxMove < 20) { + dwFileMove[pgn.nMaxMove] = 0; + } + if (EccoApi.Available()) { + dwEccoIndex = EccoApi.EccoIndex((const char *) dwFileMove); + strcpy(pgn.szEcco, (const char *) &dwEccoIndex); + strcpy(pgn.szOpen, EccoApi.EccoOpening(dwEccoIndex)); + strcpy(pgn.szVar, EccoApi.EccoVariation(dwEccoIndex)); + } + + fclose(fp); + return (pgn.Write(szPgnFile) ? CHE2PGN_OK : CHE2PGN_ERROR_CREATE); +} + +#ifndef MXQFCONV_EXE + +int main(int argc, char **argv) { + EccoApiStruct EccoApi; + char szLibEccoPath[1024]; + + if (argc < 2) { + printf("=== CHE->PGN Convertor ===\n"); + printf("Usage: CHE2PGN CHE-File [PGN-File]\n"); + return 0; + } + + PreGenInit(); + ChineseInit(); + LocatePath(szLibEccoPath, cszLibEccoFile); + EccoApi.Startup(szLibEccoPath); + + switch (Che2Pgn(argv[1], argc == 2 ? "CHE2PGN.PGN" : argv[2], EccoApi)) { + case CHE2PGN_ERROR_OPEN: + printf("%s: File Opening Error!\n", argv[1]); + break; + case CHE2PGN_ERROR_CREATE: + printf("File Creation Error!\n"); + break; + case CHE2PGN_OK: +#ifdef _WIN32 + if (argc == 2) { + ShellExecute(NULL, NULL, "CHE2PGN.PGN", NULL, NULL, SW_SHOW); + } +#endif + break; + } + EccoApi.Shutdown(); + return 0; +} + +#endif diff --git a/XQFTOOLS/chn2pgn.cpp b/XQFTOOLS/chn2pgn.cpp new file mode 100644 index 0000000..61ca41e --- /dev/null +++ b/XQFTOOLS/chn2pgn.cpp @@ -0,0 +1,144 @@ +/* +CHN->PGN Convertor - a Chinese Chess Score Convertion Program +Designed by Morning Yellow, Version: 3.14, Last Modified: Jun. 2008 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#ifdef _WIN32 + #include +#else + #include +#endif +#include "../base/base2.h" +#include "../eleeye/position.h" +#include "../cchess/cchess.h" +#include "../cchess/ecco.h" +#include "../cchess/pgnfile.h" + +static const int CHN2PGN_ERROR_OPEN = -2; +static const int CHN2PGN_ERROR_CREATE = -1; +static const int CHN2PGN_OK = 0; + +struct ChnRecord { + uint16_t wReserved1[2]; + uint16_t wxSrc, wySrc, wxDst, wyDst; + uint16_t wReserved2[10]; +}; + +int Chn2Pgn(const char *szChnFile, const char *szPgnFile, const EccoApiStruct &EccoApi) { + int mv, nStatus; + bool bRead, bFlip; + PgnFileStruct pgn; + PositionStruct pos; + ChnRecord Chn; + FILE *fp; + uint32_t dwEccoIndex, dwFileMove[20]; + + fp = fopen(szChnFile, "rb"); + if (fp == NULL) { + return CHN2PGN_ERROR_OPEN; + } + + pgn.posStart.FromFen(cszStartFen); + pos = pgn.posStart; + + bRead = bFlip = false; + fseek(fp, 188, SEEK_SET); + while (fread(&Chn, sizeof(ChnRecord), 1, fp) > 0) { + if (!bRead) { + bRead = true; + if (Chn.wySrc < 5) { + bFlip = true; + } + } + if (bFlip) { + Chn.wxSrc = 8 - Chn.wxSrc; + Chn.wySrc = 9 - Chn.wySrc; + Chn.wxDst = 8 - Chn.wxDst; + Chn.wyDst = 9 - Chn.wyDst; + } + mv = MOVE(COORD_XY(Chn.wxSrc + FILE_LEFT, Chn.wySrc + RANK_TOP), + COORD_XY(Chn.wxDst + FILE_LEFT, Chn.wyDst + RANK_TOP)); + mv &= 0xffff; // 防止TryMove时数组越界 + pgn.nMaxMove ++; + if (pgn.nMaxMove <= 20) { + dwFileMove[pgn.nMaxMove - 1] = Move2File(mv, pos); + } + // 联众可能允许把将吃掉,但ElephantEye不允许,所以跳过非法着法 + if (TryMove(pos, nStatus, mv)) { + pgn.wmvMoveTable[pgn.nMaxMove] = mv; + } else { + pgn.nMaxMove --; + } + if (pos.nMoveNum == MAX_MOVE_NUM) { + pos.SetIrrev(); + } + } + + if (pgn.nMaxMove < 20) { + dwFileMove[pgn.nMaxMove] = 0; + } + if (EccoApi.Available()) { + dwEccoIndex = EccoApi.EccoIndex((const char *) dwFileMove); + strcpy(pgn.szEcco, (const char *) &dwEccoIndex); + strcpy(pgn.szOpen, EccoApi.EccoOpening(dwEccoIndex)); + strcpy(pgn.szVar, EccoApi.EccoVariation(dwEccoIndex)); + } + + fclose(fp); + return (pgn.Write(szPgnFile) ? CHN2PGN_OK : CHN2PGN_ERROR_CREATE); +} + +#ifndef MXQFCONV_EXE + +int main(int argc, char **argv) { + EccoApiStruct EccoApi; + char szLibEccoPath[1024]; + + if (argc < 2) { + printf("=== CHN->PGN Convertor ===\n"); + printf("Usage: CHN2PGN CHN-File [PGN-File]\n"); + return 0; + } + + PreGenInit(); + ChineseInit(); + LocatePath(szLibEccoPath, cszLibEccoFile); + EccoApi.Startup(szLibEccoPath); + + switch (Chn2Pgn(argv[1], argc == 2 ? "CHN2PGN.PGN" : argv[2], EccoApi)) { + case CHN2PGN_ERROR_OPEN: + printf("%s: File Opening Error!\n", argv[1]); + break; + case CHN2PGN_ERROR_CREATE: + printf("File Creation Error!\n"); + break; + case CHN2PGN_OK: +#ifdef _WIN32 + if (argc == 2) { + ShellExecute(NULL, NULL, "CHN2PGN.PGN", NULL, NULL, SW_SHOW); + } +#endif + break; + } + EccoApi.Shutdown(); + return 0; +} + +#endif diff --git a/XQFTOOLS/makefile.sh b/XQFTOOLS/makefile.sh new file mode 100644 index 0000000..3622976 --- /dev/null +++ b/XQFTOOLS/makefile.sh @@ -0,0 +1,4 @@ +g++ -DNDEBUG -O4 -Wall -ldl -oXQF2PGN.EXE ../eleeye/pregen.cpp ../eleeye/position.cpp ../eleeye/genmoves.cpp ../cchess/cchess.cpp ../cchess/pgnfile.cpp xqf2pgn.cpp +g++ -DNDEBUG -O4 -Wall -ldl -oMXQ2PGN.EXE ../eleeye/pregen.cpp ../eleeye/position.cpp ../eleeye/genmoves.cpp ../cchess/cchess.cpp ../cchess/pgnfile.cpp mxq2pgn.cpp +g++ -DNDEBUG -O4 -Wall -ldl -oCHE2PGN.EXE ../eleeye/pregen.cpp ../eleeye/position.cpp ../eleeye/genmoves.cpp ../cchess/cchess.cpp ../cchess/pgnfile.cpp che2pgn.cpp +g++ -DNDEBUG -O4 -Wall -oPGN2XQF.EXE ../eleeye/pregen.cpp ../eleeye/position.cpp ../eleeye/genmoves.cpp ../cchess/cchess.cpp ../cchess/pgnfile.cpp pgn2xqf.cpp \ No newline at end of file diff --git a/XQFTOOLS/mxq2pgn.cpp b/XQFTOOLS/mxq2pgn.cpp new file mode 100644 index 0000000..2fd36b9 --- /dev/null +++ b/XQFTOOLS/mxq2pgn.cpp @@ -0,0 +1,171 @@ +/* +MXQ->PGN Convertor - a Chinese Chess Score Convertion Program +Designed by Morning Yellow, Version: 3.14, Last Modified: Jun. 2008 +Copyright (C) 2004-2008 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#ifdef _WIN32 + #include +#else + #include +#endif +#include "../base/base2.h" +#include "../base/parse.h" +#include "../eleeye/position.h" +#include "../cchess/cchess.h" +#include "../cchess/ecco.h" +#include "../cchess/pgnfile.h" + +inline void ReadRecord(FILE *fp, char *sz) { + uint8_t ucLen; + fread(&ucLen, 1, 1, fp); + fread(sz, 1, ucLen, fp); + sz[ucLen] = '\0'; +} + +static const int MXQ2PGN_ERROR_OPEN = -2; +static const int MXQ2PGN_ERROR_CREATE = -1; +static const int MXQ2PGN_OK = 0; + +int Mxq2Pgn(const char *szMxqFile, const char *szPgnFile, const EccoApiStruct &EccoApi) { + int i, mv, nStatus; + char *lpEvent; + char szRecord[256], szComment[256]; + PgnFileStruct pgn; + PositionStruct pos; + + FILE *fp; + uint32_t dwEccoIndex, dwFileMove[20]; + + fp = fopen(szMxqFile, "rb"); + if (fp == NULL) { + return MXQ2PGN_ERROR_OPEN; + } + + ReadRecord(fp, pgn.szSite); + ReadRecord(fp, pgn.szDate); + ReadRecord(fp, pgn.szEvent); + lpEvent = pgn.szEvent; + if (false) { + } else if (StrScanSkip(lpEvent, "-胜-")) { + pgn.nResult = 1; + } else if (StrScanSkip(lpEvent, "-衊-")) { + pgn.nResult = 1; + } else if (StrScanSkip(lpEvent, "-和-")) { + pgn.nResult = 2; + } else if (StrScanSkip(lpEvent, "-㎝-")) { + pgn.nResult = 2; + } else if (StrScanSkip(lpEvent, "-负-")) { + pgn.nResult = 3; + } else if (StrScanSkip(lpEvent, "-璽-")) { + pgn.nResult = 3; + } else if (StrScanSkip(lpEvent, "-負-")) { + pgn.nResult = 3; + } else { + pgn.nResult = 0; + } + if (pgn.nResult != 0) { + strcpy(pgn.szRed, pgn.szEvent); + *(pgn.szRed + (lpEvent - pgn.szEvent - 4)) = '\0'; + strcpy(pgn.szBlack, lpEvent); + } + ReadRecord(fp, pgn.szRedElo); + ReadRecord(fp, pgn.szBlackElo); + for (i = 0; i < 5; i ++) { + ReadRecord(fp, szRecord); + } + ReadRecord(fp, szComment); + ReadRecord(fp, szRecord); + + pgn.posStart.FromFen(cszStartFen); + pos = pgn.posStart; + + ReadRecord(fp, szRecord); + while (!StrEqv(szRecord, "Ends") && pgn.nMaxMove < MAX_MOVE_LEN - 1) { + mv = MOVE(COORD_XY(szRecord[0] - '0' + 3, 'J' - szRecord[1] + 3), COORD_XY(szRecord[3] - '0' + 3, 'J' - szRecord[4] + 3)); + mv &= 0xffff; // 防止TryMove时数组越界 + pgn.nMaxMove ++; + if (pgn.nMaxMove <= 20) { + dwFileMove[pgn.nMaxMove - 1] = Move2File(mv, pos); + } + // 弈天可能允许把将吃掉,但ElephantEye不允许,所以跳过非法着法 + if (TryMove(pos, nStatus, mv)) { + pgn.wmvMoveTable[pgn.nMaxMove] = mv; + } else { + pgn.nMaxMove --; + } + if (pos.nMoveNum == MAX_MOVE_NUM) { + pos.SetIrrev(); + } + ReadRecord(fp, szRecord); + } + pgn.szCommentTable[pgn.nMaxMove] = new char[256]; + strcpy(pgn.szCommentTable[pgn.nMaxMove], szComment); + + if (pgn.nMaxMove < 20) { + dwFileMove[pgn.nMaxMove] = 0; + } + if (EccoApi.Available()) { + dwEccoIndex = EccoApi.EccoIndex((const char *) dwFileMove); + strcpy(pgn.szEcco, (const char *) &dwEccoIndex); + strcpy(pgn.szOpen, EccoApi.EccoOpening(dwEccoIndex)); + strcpy(pgn.szVar, EccoApi.EccoVariation(dwEccoIndex)); + } + + fclose(fp); + return (pgn.Write(szPgnFile) ? MXQ2PGN_OK : MXQ2PGN_ERROR_CREATE); +} + +#ifndef MXQFCONV_EXE + +int main(int argc, char **argv) { + EccoApiStruct EccoApi; + char szLibEccoPath[1024]; + + if (argc < 2) { + printf("=== MXQ->PGN Convertor ===\n"); + printf("Usage: MXQ2PGN MXQ-File [PGN-File]\n"); + return 0; + } + + PreGenInit(); + ChineseInit(); + LocatePath(szLibEccoPath, cszLibEccoFile); + EccoApi.Startup(szLibEccoPath); + + switch (Mxq2Pgn(argv[1], argc == 2 ? "MXQ2PGN.PGN" : argv[2], EccoApi)) { + case MXQ2PGN_ERROR_OPEN: + printf("%s: File Opening Error!\n", argv[1]); + break; + case MXQ2PGN_ERROR_CREATE: + printf("File Creation Error!\n"); + break; + case MXQ2PGN_OK: +#ifdef _WIN32 + if (argc == 2) { + ShellExecute(NULL, NULL, "MXQ2PGN.PGN", NULL, NULL, SW_SHOW); + } +#endif + break; + } + EccoApi.Shutdown(); + return 0; +} + +#endif diff --git a/XQFTOOLS/pgn2xqf.cpp b/XQFTOOLS/pgn2xqf.cpp new file mode 100644 index 0000000..513accd --- /dev/null +++ b/XQFTOOLS/pgn2xqf.cpp @@ -0,0 +1,146 @@ +/* +PGN->XQF Convertor - a Chinese Chess Score Convertion Program +Designed by Morning Yellow, Version: 2.02, Last Modified: Apr. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#ifdef _WIN32 + #include +#endif +#include "../base/base.h" +#include "../eleeye/position.h" +#include "../cchess/cchess.h" +#include "../cchess/pgnfile.h" +#include "xqffile.h" + +static const char ccSquare2Xqf[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 9, 19, 29, 39, 49, 59, 69, 79, 89, -1, -1, -1, -1, + -1, -1, -1, 8, 18, 28, 38, 48, 58, 68, 78, 88, -1, -1, -1, -1, + -1, -1, -1, 7, 17, 27, 37, 47, 57, 67, 77, 87, -1, -1, -1, -1, + -1, -1, -1, 6, 16, 26, 36, 46, 56, 66, 76, 86, -1, -1, -1, -1, + -1, -1, -1, 5, 15, 25, 35, 45, 55, 65, 75, 85, -1, -1, -1, -1, + -1, -1, -1, 4, 14, 24, 34, 44, 54, 64, 74, 84, -1, -1, -1, -1, + -1, -1, -1, 3, 13, 23, 33, 43, 53, 63, 73, 83, -1, -1, -1, -1, + -1, -1, -1, 2, 12, 22, 32, 42, 52, 62, 72, 82, -1, -1, -1, -1, + -1, -1, -1, 1, 11, 21, 31, 41, 51, 61, 71, 81, -1, -1, -1, -1, + -1, -1, -1, 0, 10, 20, 30, 40, 50, 60, 70, 80, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static const int cpcXqf2Piece[32] = { + 23, 21, 19, 17, 16, 18, 20, 22, 24, 25, 26, 27, 28, 29, 30, 31, + 39, 37, 35, 33, 32, 34, 36, 38, 40, 41, 42, 43, 44, 45, 46, 47 +}; + +static const int cnResultTrans[4] = { + 0, 1, 3, 2 +}; + +inline void SetXqfString(char *sz, const char *szValue, int nMaxLen) { + int nLen; + nLen = MIN((int) strlen(szValue), nMaxLen - 1); + sz[0] = nLen; + strncpy(sz + 1, szValue, nLen); +} + +int main(char argc, char **argv) { + int i, nCommentLen; + char szRed[MAX_STR_LEN * 2], szBlack[MAX_STR_LEN * 2]; + FILE *fp; + PgnFileStruct pgn; + XqfHeaderStruct xqfhd; + XqfMoveStruct xqfmv; + if (argc <= 1) { + printf("=== PGN->XQF Convertor ===\n"); + printf("Usage: PGN2XQF PGN-File [XQF-File]\n"); + return 0; + } + PreGenInit(); + ChineseInit(); + if (!pgn.Read(argv[1])) { + printf("%s: File Not Found or Not a Chinese-Chess-PGN File!\n", argv[1]); + return 0; + } + if (argc == 2) { + fp = fopen("PGN2XQF.XQF", "wb"); + } else { + fp = fopen(argv[2], "wb"); + } + if (fp == NULL) { + printf("File Creation Error!\n"); + return 0; + } + memset(xqfhd.szTag, 0, sizeof(XqfHeaderStruct)); + xqfhd.szTag[0] = 'X'; + xqfhd.szTag[1] = 'Q'; + xqfhd.szTag[2] = 10; + for (i = 0; i < 32; i ++) { + xqfhd.szPiecePos[i] = ccSquare2Xqf[pgn.posStart.ucsqPieces[cpcXqf2Piece[i]]]; + } + xqfhd.szResult[3] = cnResultTrans[pgn.nResult]; + xqfhd.szSetUp[0] = 2; + SetXqfString(xqfhd.szEvent, pgn.szEvent, sizeof(xqfhd.szEvent)); + SetXqfString(xqfhd.szDate, pgn.szDate, sizeof(xqfhd.szDate)); + SetXqfString(xqfhd.szSite, pgn.szSite, sizeof(xqfhd.szSite)); + if (pgn.szRedTeam[0] == '\0') { + SetXqfString(xqfhd.szRed, pgn.szRed, sizeof(xqfhd.szRed)); + } else { + sprintf(szRed, "%s %s", pgn.szRedTeam, pgn.szRed); + SetXqfString(xqfhd.szRed, szRed, sizeof(xqfhd.szRed)); + } + if (pgn.szBlackTeam[0] == '\0') { + SetXqfString(xqfhd.szBlack, pgn.szBlack, sizeof(xqfhd.szBlack)); + } else { + sprintf(szBlack, "%s %s", pgn.szBlackTeam, pgn.szBlack); + SetXqfString(xqfhd.szBlack, szBlack, sizeof(xqfhd.szBlack)); + } + fwrite(&xqfhd, sizeof(xqfhd), 1, fp); + memset(&xqfhd, 0, sizeof(xqfhd)); + fwrite(&xqfhd, sizeof(xqfhd), 1, fp); + for (i = 0; i <= pgn.nMaxMove; i ++) { + xqfmv.ucTag = (i == pgn.nMaxMove ? 0 : 240); + if (i == 0) { + xqfmv.ucSrc = 24; + xqfmv.ucDst = 32; + xqfmv.ucReserved = 255; + } else { + xqfmv.ucSrc = 24 + ccSquare2Xqf[SRC(pgn.wmvMoveTable[i])]; + xqfmv.ucDst = 32 + ccSquare2Xqf[DST(pgn.wmvMoveTable[i])]; + xqfmv.ucReserved = 0; + } + fwrite(&xqfmv, sizeof(XqfMoveStruct), 1, fp); + nCommentLen = (pgn.szCommentTable[i] == NULL ? 0 : strlen(pgn.szCommentTable[i])); + fwrite(&nCommentLen, sizeof(int), 1, fp); + if (pgn.szCommentTable[i] != NULL) { + fwrite(pgn.szCommentTable[i], nCommentLen, 1, fp); + } + } + fclose(fp); +#ifdef _WIN32 + if (argc == 2) { + ShellExecute(NULL, NULL, "PGN2XQF.XQF", NULL, NULL, SW_SHOW); + } +#endif + return 0; +} diff --git a/XQFTOOLS/xqf2pgn.cpp b/XQFTOOLS/xqf2pgn.cpp new file mode 100644 index 0000000..3e4ca06 --- /dev/null +++ b/XQFTOOLS/xqf2pgn.cpp @@ -0,0 +1,286 @@ +/* +XQF->PGN Convertor - a Chinese Chess Score Convertion Program +Designed by Morning Yellow, Version: 2.1, Last Modified: May 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#ifdef _WIN32 + #include +#else + #include +#endif +#include "../base/base2.h" +#include "../eleeye/position.h" +#include "../cchess/cchess.h" +#include "../cchess/ecco.h" +#include "../cchess/pgnfile.h" +#include "xqffile.h" + +static const int cnResultTrans[4] = { + 0, 1, 3, 2 +}; + +static const unsigned char cucsqXqf2Square[96] = { + 0xc3, 0xb3, 0xa3, 0x93, 0x83, 0x73, 0x63, 0x53, 0x43, 0x33, + 0xc4, 0xb4, 0xa4, 0x94, 0x84, 0x74, 0x64, 0x54, 0x44, 0x34, + 0xc5, 0xb5, 0xa5, 0x95, 0x85, 0x75, 0x65, 0x55, 0x45, 0x35, + 0xc6, 0xb6, 0xa6, 0x96, 0x86, 0x76, 0x66, 0x56, 0x46, 0x36, + 0xc7, 0xb7, 0xa7, 0x97, 0x87, 0x77, 0x67, 0x57, 0x47, 0x37, + 0xc8, 0xb8, 0xa8, 0x98, 0x88, 0x78, 0x68, 0x58, 0x48, 0x38, + 0xc9, 0xb9, 0xa9, 0x99, 0x89, 0x79, 0x69, 0x59, 0x49, 0x39, + 0xca, 0xba, 0xaa, 0x9a, 0x8a, 0x7a, 0x6a, 0x5a, 0x4a, 0x3a, + 0xcb, 0xbb, 0xab, 0x9b, 0x8b, 0x7b, 0x6b, 0x5b, 0x4b, 0x3b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const int cpcXqf2Piece[32] = { + 23, 21, 19, 17, 16, 18, 20, 22, 24, 25, 26, 27, 28, 29, 30, 31, + 39, 37, 35, 33, 32, 34, 36, 38, 40, 41, 42, 43, 44, 45, 46, 47 +}; + +// 密钥流掩码 +static const char *const cszEncStreamMask = "[(C) Copyright Mr. Dong Shiwei.]"; + +inline int Square54Plus221(int x) { + return x * x * 54 + 221; +} + +inline void ReadAndDecrypt(FILE *fp, void *lp, int nLen, const int *nEncStream, int &nEncIndex) { + int i; + fread(lp, nLen, 1, fp); + for (i = 0; i < nLen; i ++) { + ((uint8_t *) lp)[i] -= nEncStream[nEncIndex]; + nEncIndex = (nEncIndex + 1) % 32; + } +} + +inline void GetXqfString(char *szPgn, const char *szXqf) { + strncpy(szPgn, szXqf + 1, szXqf[0]); + szPgn[szXqf[0]] = '\0'; +} + +static const int XQF2PGN_ERROR_OPEN = -3; +static const int XQF2PGN_ERROR_FORMAT = -2; +static const int XQF2PGN_ERROR_CREATE = -1; +static const int XQF2PGN_OK = 0; + +int Xqf2Pgn(const char *szXqfFile, const char *szPgnFile, const EccoApiStruct &EccoApi) { + int i, nArg0, nArgs[4]; + int nCommentLen, mv, nStatus; + bool bHasNext; + PgnFileStruct pgn; + PositionStruct pos; + + FILE *fp; + XqfHeaderStruct xqfhd; + XqfMoveStruct xqfmv; + // 版本号和加密偏移值 + int nXqfVer, nPieceOff, nSrcOff, nDstOff, nCommentOff; + // 密钥流 + int nEncStream[32]; + // 密钥流索引号 + int nEncIndex; + // 局面初始位置 + int nPiecePos[32]; + + uint32_t dwEccoIndex, dwFileMove[20]; + + fp = fopen(szXqfFile, "rb"); + if (fp == NULL) { + return XQF2PGN_ERROR_OPEN; + } + fread(&xqfhd, sizeof(xqfhd), 1, fp); + fseek(fp, sizeof(xqfhd), SEEK_CUR); + if (xqfhd.szTag[0] == 'X' && xqfhd.szTag[1] == 'Q') { + // PGN文件可以打开,现在正式解析XQF文件 + nXqfVer = xqfhd.szTag[2]; + if (nXqfVer < 11) { + nPieceOff = nSrcOff = nDstOff = nCommentOff = 0; + for (i = 0; i < 32; i ++) { + nEncStream[i] = 0; + } + } else { + // 局面初始位置的加密偏移值 + nPieceOff = (uint8_t) (Square54Plus221((uint8_t) xqfhd.szTag[13]) * (uint8_t) xqfhd.szTag[13]); + // 着法起点的加密偏移值 + nSrcOff = (uint8_t) (Square54Plus221((uint8_t) xqfhd.szTag[14]) * nPieceOff); + // 着法终点的加密偏移值 + nDstOff = (uint8_t) (Square54Plus221((uint8_t) xqfhd.szTag[15]) * nSrcOff); + // 注释的加密偏移值 + nCommentOff = ((uint8_t) xqfhd.szTag[12] * 256 + (uint8_t) xqfhd.szTag[13]) % 32000 + 767; + // 基本掩码 + nArg0 = xqfhd.szTag[3]; + // 密钥 = 前段密钥 | (后段密钥 & 基本掩码) + for (i = 0; i < 4; i ++) { + nArgs[i] = xqfhd.szTag[8 + i] | (xqfhd.szTag[12 + i] & nArg0); + } + // 密钥流 = 密钥 & 密钥流掩码 + for (i = 0; i < 32; i ++) { + nEncStream[i] = (uint8_t) (nArgs[i % 4] & cszEncStreamMask[i]); + } + } + nEncIndex = 0; + + // 记录棋谱信息 + if (xqfhd.szEvent[0] == 0) { + GetXqfString(pgn.szEvent, xqfhd.szTitle); + } else { + GetXqfString(pgn.szEvent, xqfhd.szEvent); + } + GetXqfString(pgn.szDate, xqfhd.szDate); + GetXqfString(pgn.szSite, xqfhd.szSite); + GetXqfString(pgn.szRed, xqfhd.szRed); + GetXqfString(pgn.szBlack, xqfhd.szBlack); + pgn.nResult = cnResultTrans[(int) xqfhd.szResult[3]]; + + if (xqfhd.szSetUp[0] < 2) { + // 如果是开局或者全局,那么直接设置起始局面 + pgn.posStart.FromFen(cszStartFen); + } else { + // 如果是中局或者排局,那么根据"xqfhd.szPiecePos[32]"的内容摆放局面 + // 当版本号达到12时,还要进一步解密局面初始位置 + if (nXqfVer < 12) { + for (i = 0; i < 32; i ++) { + nPiecePos[i] = (uint8_t) (xqfhd.szPiecePos[i] - nPieceOff); + } + } else { + for (i = 0; i < 32; i ++) { + nPiecePos[(nPieceOff + 1 + i) % 32] = (uint8_t) (xqfhd.szPiecePos[i] - nPieceOff); + } + } + // 把"nPiecePos[32]"的数据放到"PositionStruct"中 + pgn.posStart.ClearBoard(); + for (i = 0; i < 32; i ++) { + if (nPiecePos[i] < 90) { + pgn.posStart.AddPiece(cucsqXqf2Square[nPiecePos[i]], cpcXqf2Piece[i]); + } + } + pgn.posStart.SetIrrev(); + } + pos = pgn.posStart; + + bHasNext = true; + while (bHasNext && pgn.nMaxMove < MAX_MOVE_LEN) { + // 读取着法记录 + if (nXqfVer < 11) { + fread(&xqfmv, sizeof(xqfmv), 1, fp); + fread(&nCommentLen, sizeof(int), 1, fp); + if ((xqfmv.ucTag & 0xf0) == 0) { + bHasNext = false; + } + } else { + ReadAndDecrypt(fp, &xqfmv, sizeof(xqfmv), nEncStream, nEncIndex); + if ((xqfmv.ucTag & 0x20) != 0) { + ReadAndDecrypt(fp, &nCommentLen, sizeof(int), nEncStream, nEncIndex); + nCommentLen -= nCommentOff; + } else { + nCommentLen = 0; + } + if ((xqfmv.ucTag & 0x80) == 0) { + bHasNext = false; + } + } + if (pgn.nMaxMove > 0) { + // 记录着法 + mv = MOVE(cucsqXqf2Square[(uint8_t) (xqfmv.ucSrc - 24 - nSrcOff)], cucsqXqf2Square[(uint8_t) (xqfmv.ucDst - 32 - nDstOff)]); + if (pgn.nMaxMove == 1) { + if ((pgn.posStart.ucpcSquares[SRC(mv)] & 32) != 0) { + pgn.posStart.ChangeSide(); + pos.ChangeSide(); + } + } + if (xqfhd.szSetUp[0] < 2 && pgn.nMaxMove <= 20) { + dwFileMove[pgn.nMaxMove - 1] = Move2File(mv, pos); + } + TryMove(pos, nStatus, mv); + pgn.wmvMoveTable[pgn.nMaxMove] = mv; + if (pos.nMoveNum == MAX_MOVE_NUM) { + pos.SetIrrev(); + } + } + if (nCommentLen > 0) { + pgn.szCommentTable[pgn.nMaxMove] = new char[nCommentLen + 1]; + ReadAndDecrypt(fp, pgn.szCommentTable[pgn.nMaxMove], nCommentLen, nEncStream, nEncIndex); + pgn.szCommentTable[pgn.nMaxMove][nCommentLen] = '\0'; + } + pgn.nMaxMove ++; + } + pgn.nMaxMove --; + + // 解析ECCO + if (xqfhd.szSetUp[0] < 2) { + if (pgn.nMaxMove < 20) { + dwFileMove[pgn.nMaxMove] = 0; + } + if (EccoApi.Available()) { + dwEccoIndex = EccoApi.EccoIndex((const char *) dwFileMove); + strcpy(pgn.szEcco, (const char *) &dwEccoIndex); + strcpy(pgn.szOpen, EccoApi.EccoOpening(dwEccoIndex)); + strcpy(pgn.szVar, EccoApi.EccoVariation(dwEccoIndex)); + } + } + + fclose(fp); + return (pgn.Write(szPgnFile) ? XQF2PGN_OK : XQF2PGN_ERROR_CREATE); + } else { + fclose(fp); + return XQF2PGN_ERROR_FORMAT; + } +} + +#ifndef MXQFCONV_EXE + +int main(int argc, char **argv) { + EccoApiStruct EccoApi; + char szLibEccoPath[1024]; + + if (argc < 2) { + printf("=== XQF->PGN Convertor ===\n"); + printf("Usage: XQF2PGN XQF-File [PGN-File]\n"); + return 0; + } + + PreGenInit(); + ChineseInit(); + LocatePath(szLibEccoPath, cszLibEccoFile); + EccoApi.Startup(szLibEccoPath); + + switch (Xqf2Pgn(argv[1], argc == 2 ? "XQF2PGN.PGN" : argv[2], EccoApi)) { + case XQF2PGN_ERROR_OPEN: + printf("%s: File Opening Error!\n", argv[1]); + break; + case XQF2PGN_ERROR_FORMAT: + printf("%s: Not an XQF File!\n", argv[1]); + break; + case XQF2PGN_ERROR_CREATE: + printf("File Creation Error!\n"); + break; + case XQF2PGN_OK: +#ifdef _WIN32 + if (argc == 2) { + ShellExecute(NULL, NULL, "XQF2PGN.PGN", NULL, NULL, SW_SHOW); + } +#endif + break; + } + EccoApi.Shutdown(); + return 0; +} + +#endif diff --git a/XQFTOOLS/xqffile.h b/XQFTOOLS/xqffile.h new file mode 100644 index 0000000..8cf5dc6 --- /dev/null +++ b/XQFTOOLS/xqffile.h @@ -0,0 +1,49 @@ +/* +XQF->PGN Convertor - a Chinese Chess Score Convertion Program +Designed by Morning Yellow, Version: 2.02, Last Modified: Apr. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef XQFFILE_H +#define XQFFILE_H + +struct XqfHeaderStruct { + char szTag[16]; + char szPiecePos[32]; + char szResult[16]; + char szSetUp[16]; + char szTitle[64]; + char szReserved1[64]; + char szEvent[64]; + char szDate[16]; + char szSite[16]; + char szRed[16]; + char szBlack[16]; + char szRuleTime[64]; + char szRedTime[16]; + char szBlackTime[16]; + char szReserved2[32]; + char szAnnotator[16]; + char szAuthor[16]; + char szReserved3[16]; +}; // xqfhd + +struct XqfMoveStruct { + unsigned char ucSrc, ucDst, ucTag, ucReserved; +}; // xqfmv + +#endif diff --git a/background.gif b/background.gif new file mode 100644 index 0000000..b981179 Binary files /dev/null and b/background.gif differ diff --git a/base/ALPHABLT.BAS b/base/ALPHABLT.BAS new file mode 100644 index 0000000..c5cf6ec --- /dev/null +++ b/base/ALPHABLT.BAS @@ -0,0 +1,75 @@ +Attribute VB_Name = "mdlAlphaBlt" +Option Explicit + +Private Const WS_VISIBLE As Long = &H10000000 +Private Const WS_POPUP As Long = &H80000000 + +Private Type WndClassEx + cbSize As Long + style As Long + lpfnWndProc As Long + cbClsExtra As Long + cbWndExtra As Long + hInstance As Long + hIcon As Long + hCursor As Long + hbrBackground As Long + lpszMenuName As String + lpszClassName As String + hIconSm As Long +End Type + +Private Declare Sub AlphaBlt Lib "ALPHABLT.DLL" Alias "_AlphaBlt@44" (ByVal hdcDest As Long, ByVal xDest As Long, ByVal yDest As Long, ByVal nWidth As Long, ByVal nHeight As Long, _ + ByVal hdcSrc As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal hdcAlpha As Long, ByVal xAlpha As Long, ByVal yAlpha As Long) +Private Declare Function DefWindowProcA Lib "USER32.DLL" (ByVal hWnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long +Private Declare Sub RegisterClassExA Lib "USER32.DLL" (ByRef wcx As WndClassEx) +Private Declare Function CreateWindowExA Lib "USER32.DLL" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, _ + ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hWndParent As Long, ByVal hMenu As Long, ByVal hInstance As Long, ByVal lpParam As Long) As Long +Private Declare Function GetDC Lib "USER32.DLL" (ByVal hWnd As Long) As Long +Private Declare Sub ReleaseDC Lib "USER32.DLL" (ByVal hWnd As Long, ByVal hdc As Long) +Private Declare Function CreateCompatibleDC Lib "GDI32.DLL" (ByVal hdc As Long) As Long +Private Declare Sub SelectObject Lib "GDI32.DLL" (ByVal hdc As Long, ByVal hgdiobj As Long) +Private Declare Sub DeleteDC Lib "GDI32.DLL" (ByVal hdc As Long) + +Private Function WndProc(ByVal hWnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long + +WndProc = DefWindowProcA(hWnd, msg, wParam, lParam) + +End Function + +Public Function TransparentWindow(ByVal hInstance As Long, ByVal hbmpSrc As Long, ByVal hbmpAlpha As Long, ByVal nWidth As Integer, ByVal nHeight As Integer) + +Dim hWnd As Long, hdcDest As Long, hdcSrc As Long, hdcAlpha As Long +Dim nWidthScreen As Integer, nHeightScreen As Integer, nWidthBitmap As Integer, nHeightBitmap As Integer +Dim wcx As WndClassEx + +nWidthScreen = Fix(Screen.Width / Screen.TwipsPerPixelX) +nHeightScreen = Fix(Screen.Height / Screen.TwipsPerPixelY) +nWidthBitmap = Fix(nWidth / Screen.TwipsPerPixelX) +nHeightBitmap = Fix(nHeight / Screen.TwipsPerPixelY) +wcx.cbSize = Len(wcx) +wcx.style = 0 +wcx.lpfnWndProc = Assign(AddressOf WndProc) +wcx.cbClsExtra = 0 +wcx.cbWndExtra = 0 +wcx.hInstance = hInstance +wcx.hIcon = 0 +wcx.hCursor = 0 +wcx.hbrBackground = 0 +wcx.lpszMenuName = vbNullString +wcx.lpszClassName = "TransparentWindow" +wcx.hIconSm = 0 +RegisterClassExA wcx +hWnd = CreateWindowExA(0, "TransparentWindow", vbNullString, WS_VISIBLE Or WS_POPUP, (nWidthScreen - nWidthBitmap) \ 2, (nHeightScreen - nHeightBitmap) \ 2, nWidthBitmap, nHeightBitmap, 0, 0, hInstance, 0) +hdcDest = GetDC(hWnd) +hdcSrc = CreateCompatibleDC(hdcDest) +hdcAlpha = CreateCompatibleDC(hdcDest) +SelectObject hdcSrc, hbmpSrc +SelectObject hdcAlpha, hbmpAlpha +AlphaBlt hdcDest, 0, 0, nWidthBitmap, nHeightBitmap, hdcSrc, 0, 0, hdcAlpha, 0, 0 +DeleteDC hdcSrc +DeleteDC hdcAlpha +ReleaseDC hWnd, hdcDest +TransparentWindow = hWnd + +End Function diff --git a/base/ALPHABLT.C b/base/ALPHABLT.C new file mode 100644 index 0000000..c0d4a62 --- /dev/null +++ b/base/ALPHABLT.C @@ -0,0 +1,50 @@ +#include + +__declspec(dllexport) VOID WINAPI AlphaBlt( + HDC hdcDest, int xDest, int yDest, int nWidth, int nHeight, + HDC hdcSrc, int xSrc, int ySrc, HDC hdcAlpha, int xAlpha, int yAlpha); + +VOID WINAPI AlphaBlt(HDC hdcDest, int xDest, int yDest, int nWidth, int nHeight, + HDC hdcSrc, int xSrc, int ySrc, HDC hdcAlpha, int xAlpha, int yAlpha) { + int i, n; + HDC hdc1Dest, hdc1Src, hdc1Alpha; + HBITMAP hbmpDest, hbmpSrc, hbmpAlpha; + COLORREF *lpDest, *lpSrc, *lpAlpha; + BITMAPINFO bmi; + + memset(&bmi, 0, sizeof(BITMAPINFO)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = nWidth; + bmi.bmiHeader.biHeight = nHeight; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + + hdc1Dest = CreateCompatibleDC(NULL); + hdc1Src = CreateCompatibleDC(NULL); + hdc1Alpha = CreateCompatibleDC(NULL); + hbmpDest = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, &lpDest, NULL, 0); + hbmpSrc = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, &lpSrc, NULL, 0); + hbmpAlpha = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, &lpAlpha, NULL, 0); + SelectObject(hdc1Dest, hbmpDest); + SelectObject(hdc1Src, hbmpSrc); + SelectObject(hdc1Alpha, hbmpAlpha); + + BitBlt(hdc1Dest, 0, 0, nWidth, nHeight, hdcDest, xDest, yDest, SRCCOPY); + BitBlt(hdc1Src, 0, 0, nWidth, nHeight, hdcSrc, xSrc, ySrc, SRCCOPY); + BitBlt(hdc1Alpha, 0, 0, nWidth, nHeight, hdcAlpha, xAlpha, yAlpha, SRCCOPY); + n = nHeight * nWidth; + for (i = 0; i < n; i ++) { + lpDest[i] = RGB( + (GetRValue(lpDest[i]) * (256 - GetRValue(lpAlpha[i])) + GetRValue(lpSrc[i]) * GetRValue(lpAlpha[i])) >> 8, + (GetGValue(lpDest[i]) * (256 - GetGValue(lpAlpha[i])) + GetGValue(lpSrc[i]) * GetGValue(lpAlpha[i])) >> 8, + (GetBValue(lpDest[i]) * (256 - GetBValue(lpAlpha[i])) + GetBValue(lpSrc[i]) * GetBValue(lpAlpha[i])) >> 8); + } + BitBlt(hdcDest, xDest, yDest, nWidth, nHeight, hdc1Dest, 0, 0, SRCCOPY); + + DeleteObject(hbmpDest); + DeleteObject(hbmpSrc); + DeleteObject(hbmpAlpha); + DeleteDC(hdc1Dest); + DeleteDC(hdc1Src); + DeleteDC(hdc1Alpha); +} \ No newline at end of file diff --git a/base/BASE.BAS b/base/BASE.BAS new file mode 100644 index 0000000..7a9ea36 --- /dev/null +++ b/base/BASE.BAS @@ -0,0 +1,659 @@ +Attribute VB_Name = "mdlBase" +Option Explicit + +' === Begin Windows APIs === + +Public Const SW_SHOWNORMAL As Long = 1 ' ShellExecuteA +Public Const MB_ICONSTOP As Long = 16 ' MessageBeep +Public Const MB_ICONHAND As Long = 16 ' MessageBeep +Public Const MB_ICONERROR As Long = 16 ' MessageBeep +Public Const MB_ICONQUESTION As Long = 32 ' MessageBeep +Public Const MB_ICONEXCLAMATION As Long = 48 ' MessageBeep +Public Const MB_ICONWARNING As Long = 48 ' MessageBeep +Public Const MB_ICONINFORMATION As Long = 64 ' MessageBeep +Public Const MB_ICONASTERISK As Long = 64 ' MessageBeep +Public Const IDI_APPLICATION As Long = 32512 ' LoadIconA +Public Const SND_ASYNC As Long = 1 ' PlaySoundA +Public Const SND_NOWAIT As Long = 8192 ' PlaySoundA +Public Const SND_FILENAME As Long = 131072 ' PlaySoundA +Public Const SND_RESOURCE As Long = 262148 ' PlaySoundA +Public Const HWND_TOPMOST As Long = -1 ' SetWindowPos +Public Const HWND_NOTOPMOST As Long = -2 ' SetWindowPos +Public Const SWP_NOSIZE As Long = 1 ' SetWindowPos +Public Const SWP_NOMOVE As Long = 2 ' SetWindowPos +Public Const GWL_WNDPROC As Long = -4 ' SetWindowLongA +Public Const WM_LBUTTONUP As Long = 514 ' CallWindowProcA +Public Const WM_RBUTTONUP As Long = 517 ' CallWindowProcA + +Public Declare Sub Sleep Lib "KERNEL32.DLL" (ByVal dwMilliseconds As Long) +Public Declare Sub ShellExecuteA Lib "SHELL32.DLL" (ByVal hWnd As Long, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) +Public Declare Sub MessageBeep Lib "USER32.DLL" (ByVal uType As Long) +Public Declare Sub PlaySoundA Lib "WINMM.DLL" (ByVal pszSound As Long, ByVal hmod As Long, ByVal fdwSound As Long) +Public Declare Sub DestroyWindow Lib "USER32.DLL" (ByVal hWnd As Long) +Public Declare Sub SetWindowPos Lib "USER32.DLL" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) +Public Declare Function SetWindowLongA Lib "USER32.DLL" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long +Public Declare Function CallWindowProcA Lib "USER32.DLL" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long +Public Declare Function lstrlenA Lib "KERNEL32.DLL" (ByVal lpsz As Long) As Long +Public Declare Function SysAllocStringByteLen Lib "OLEAUT32.DLL" (ByVal lpsz As Long, ByVal dwLen As Long) As String + +' For Atom Subs/Functions +Public Declare Function VarPtr Lib "MSVBVM60.DLL" (ByRef lp As Any) As Long +Public Declare Function CvBStr Lib "MSVBVM60.DLL" Alias "VarPtr" (ByVal sz As String) As Long +Public Declare Function Assign Lib "MSVBVM60.DLL" Alias "VarPtr" (ByVal dw As Long) As Long +Declare Sub GetMem2 Lib "MSVBVM60.DLL" (ByVal lpSrc As Long, ByVal lpDst As Long) +Declare Sub GetMem4 Lib "MSVBVM60.DLL" (ByVal lpSrc As Long, ByVal lpDst As Long) +Declare Sub GetMem8 Lib "MSVBVM60.DLL" (ByVal lpSrc As Long, ByVal lpDst As Long) + +' For URLs +Public Type UrlType + szProto As String + szHost As String + nPort As Long + szFile As String + szLabel As String +End Type + +' For MsgBoxWithIcon/MsgBoxMute +Private Type MsgBoxParams + cbSize As Long + hwndOwner As Long + hInstance As Long + lpszText As String + lpszCaption As String + dwStyle As Long + lpszIcon As Long + dwContextHelpId As Long + lpfnMsgBoxCallback As Long + dwLanguageId As Long +End Type + +Private Const MB_USERICON As Long = 128 + +Private Declare Function LoadLibraryA Lib "KERNEL32.DLL" (ByVal lpLibFileName As String) As Long +Private Declare Sub FreeLibrary Lib "KERNEL32.DLL" (ByVal hLibModule As Long) +Private Declare Function LoadIconA Lib "USER32.DLL" (ByVal hInstance As Long, ByVal lpIconName As Long) As Long +Private Declare Function MessageBoxIndirectA Lib "USER32.DLL" (ByRef lpMsgBoxParams As MsgBoxParams) As Long + +' For Open/Save +Private Type OpenFileName + lStructSize As Long + hwndOwner As Long + hInstance As Long + lpstrFilter As String + lpstrCustomFilter As String + nMaxCustFilter As Long + nFilterIndex As Long + lpstrFile As String + nMaxFile As Long + lpstrFileTitle As String + nMaxFileTitle As Long + lpstrInitialDir As String + lpstrTitle As String + flags As Long + nFileOffset As Integer + nFileExtension As Integer + lpstrDefExt As String + lCustData As Long + lpfnHook As Long + lpTemplateName As String +End Type + +Private Const OFN_OVERWRITEPROMPT As Long = 2 +Private Const OFN_HIDEREADONLY As Long = 4 +Private Const OFN_PATHMUSTEXIST As Long = 2048 +Private Const OFN_FILEMUSTEXIST As Long = 4096 + +Private Declare Function GetOpenFileNameA Lib "COMDLG32.DLL" (ByRef lpofn As OpenFileName) As Long +Private Declare Function GetSaveFileNameA Lib "COMDLG32.DLL" (ByRef lpofn As OpenFileName) As Long + +' For BrowseForFolder +Private Type BrowseInfo + hwndOwner As Long + pidlRoot As Long + pszDisplayName As String + lpszTitle As String + ulFlags As Long + lpfn As Long + lParam As Long + iImage As Long +End Type + +Private Const BIF_RETURNONLYFSDIRS As Long = 1 + +Private Declare Function SHBrowseForFolderA Lib "SHELL32.DLL" (ByRef lpbi As BrowseInfo) As Long +Private Declare Function SHGetPathFromIDListA Lib "SHELL32.DLL" (ByVal pidl As Long, ByVal pszPath As Long) As Long + +' For TrayIcon +Private Type NotifyIconData + cbSize As Long + hWnd As Long + uID As Long + uFlags As Long + uCallbackMessage As Long + hIcon As Long + szTip As String * 64 +End Type + +Private Const NIF_MESSAGE As Long = 1 +Private Const NIF_ICON As Long = 2 +Private Const NIF_TIP As Long = 4 + +Private Const NIM_ADD As Long = 0 +Private Const NIM_MODIFY As Long = 1 +Private Const NIM_DELETE As Long = 2 + +Public Const WM_TRAY As Long = &H401 + +Private Declare Sub Shell_NotifyIcon Lib "SHELL32.DLL" Alias "Shell_NotifyIconA" (ByVal dwMessage As Long, ByRef lpData As NotifyIconData) + +' For Stdio +Private Type StartupInfo + cb As Long + lpReserved As Long + lpDesktop As Long + lpTitle As Long + dwX As Long + dwY As Long + dwXSize As Long + dwYSize As Long + dwXCountChars As Long + dwYCountChars As Long + dwFillAttribute As Long + dwFlags As Long + wShowWindow As Integer + cbReserved2 As Integer + lpReserved2 As Long + hStdInput As Long + hStdOutput As Long + hStdError As Long +End Type + +Private Const STARTF_USESTDHANDLES As Long = 256 +Private Const STD_INPUT_HANDLE As Long = -10 +Private Const STD_OUTPUT_HANDLE As Long = -11 +Private Const STD_ERROR_HANDLE As Long = -12 +Private Const INVALID_HANDLE_VALUE As Long = -1 +Private Const READ_BUFFER_LEN As Long = 65536 + +Private Declare Sub GetStartupInfoA Lib "KERNEL32.DLL" (ByRef lpStartupInfo As StartupInfo) +Private Declare Sub AllocConsole Lib "KERNEL32.DLL" () +Private Declare Sub FreeConsole Lib "KERNEL32.DLL" () +Private Declare Sub SetConsoleMode Lib "KERNEL32.DLL" (ByVal h As Long, ByVal n As Long) +Private Declare Sub ReadFile Lib "KERNEL32.DLL" (ByVal hHandle As Long, ByVal lpBuffer As Long, ByVal nNumberOfBytesToWrite As Long, ByRef lpNumberOfBytesWritten As Long, Optional ByVal lpOverlapped As Long = 0) +Private Declare Sub WriteFile Lib "KERNEL32.DLL" (ByVal hHandle As Long, ByVal lpBuffer As Long, ByVal nNumberOfBytesToWrite As Long, ByRef lpNumberOfBytesWritten As Long, Optional ByVal lpOverlapped As Long = 0) +Private Declare Function GetStdHandle Lib "KERNEL32.DLL" (ByVal nStdHandle As Long) As Long + +Private Stdio_si As StartupInfo + +' === Begin Atom Subs/Functions === + +Public Function CvI(ByVal sz As String) As Integer + +Dim w As Integer +GetMem2 CvBStr(sz), VarPtr(w) +CvI = w + +End Function + +Public Function CvL(ByVal sz As String) As Long + +Dim dw As Long +GetMem4 CvBStr(sz), VarPtr(dw) +CvL = dw + +End Function + +Public Function CvC(ByVal sz As String) As Currency + +Dim qw As Currency +GetMem8 CvBStr(sz), VarPtr(qw) +CvC = qw + +End Function + +Public Function CvS(ByVal sz As String) As Single + +Dim sf As Single +GetMem4 CvBStr(sz), VarPtr(sf) +CvS = sf + +End Function + +Public Function CvD(ByVal sz As String) As Double + +Dim df As Double +GetMem8 CvBStr(sz), VarPtr(df) +CvD = df + +End Function + +Public Function MkI(ByVal w As Integer) As String + +MkI = SysAllocStringByteLen(VarPtr(w), 2) + +End Function + +Public Function MkL(ByVal dw As Long) As String + +MkL = SysAllocStringByteLen(VarPtr(dw), 4) + +End Function + +Public Function MkC(ByVal qw As Currency) As String + +MkC = SysAllocStringByteLen(VarPtr(qw), 8) + +End Function + +Public Function MkS(ByVal sf As Single) As String + +MkS = SysAllocStringByteLen(VarPtr(sf), 4) + +End Function + +Public Function MkD(ByVal df As Double) As String + +MkD = SysAllocStringByteLen(VarPtr(df), 8) + +End Function + +Public Function MkBStr(ByVal lpsz As Long) As String + +MkBStr = SysAllocStringByteLen(lpsz, lstrlenA(lpsz)) + +End Function + +' === End Atom Subs/Functions === + +' === Begin Str2Int/Lng === + +Public Function Str2Int(ByVal sz As String, Optional ByVal nMin As Integer = -32767, Optional ByVal nMax As Integer = 32767) As Integer + +Dim df As Double +df = Val(sz) +If df < nMin Then + Str2Int = nMin +ElseIf df > nMax Then + Str2Int = nMax +Else + Str2Int = df +End If + +End Function + +Public Function Str2Lng(ByVal sz As String, Optional ByVal nMin As Long = -2147483647, Optional ByVal nMax As Long = 2147483647) As Long + +Dim df As Double +df = Val(sz) +If df < nMin Then + Str2Lng = nMin +ElseIf df > nMax Then + Str2Lng = nMax +Else + Str2Lng = df +End If + +End Function + +' === End Str2Int/Lng === + +' === Begin URLs === + +Public Function UrlEncode(ByVal sz As String) As String + +Dim szReturn As String, szChar As String +Dim i As Integer, nAscii As Integer + +szReturn = "" +For i = 1 To Len(sz) + szChar = Mid(sz, i, 1) + Select Case szChar + Case " " + szReturn = szReturn + "+" + Case "*", ".", "-", "0" To "9", "@", "A" To "Z", "_", "a" To "z" + szReturn = szReturn + szChar + Case Else + nAscii = Asc(szChar) + If nAscii < 0 Then + szReturn = szReturn + "%" + Left(Hex(nAscii), 2) + "%" + Right(Hex(nAscii), 2) + Else + szReturn = szReturn + "%" + Mid(Hex(&H100 + nAscii), 2, 2) + End If + End Select +Next +UrlEncode = szReturn + +End Function + +Public Function UrlDecode(ByVal sz As String) As String + +Dim szReturn As String, szChar As String +Dim i As Integer + +szReturn = "" +For i = 1 To Len(sz) + szChar = Mid(sz, i, 1) + Select Case szChar + Case "+" + szReturn = szReturn + " " + Case "%" + szChar = Mid(sz, i + 1, 2) + If szChar < "80" Then + szReturn = szReturn + Chr(Val("&H" + szChar)) + i = i + 2 + Else + szChar = szChar + Mid(sz, i + 4, 2) + szReturn = szReturn + Chr(Val("&H" + szChar)) + i = i + 5 + End If + Case Else + szReturn = szReturn + szChar + End Select +Next +UrlDecode = szReturn + +End Function + +Public Sub ParseUrl(ByRef url As UrlType, ByVal szUrl As String) + +Dim n As Integer, sz As String, df As Double + +n = InStr(szUrl, ":") +If n = 0 Then + url.szProto = "http" +Else + url.szProto = LCase(Left(szUrl, n - 1)) +End If +Do While Mid(szUrl, n + 1, 1) = "/" + n = n + 1 +Loop +sz = Mid(szUrl, n + 1) + +n = InStr(sz, "#") +If n = 0 Then + url.szLabel = "" +Else + url.szLabel = Mid(sz, n + 1) + sz = Left(sz, n - 1) +End If + +n = InStr(sz, "/") +If n = 0 Then + url.szFile = "" +Else + url.szFile = Mid(sz, n + 1) + If Right(url.szFile, 1) = "/" Then + url.szFile = Left(url.szFile, Len(url.szFile) - 1) + End If + sz = Left(sz, n - 1) +End If + +n = InStr(sz, ":") +If n = 0 Then + url.nPort = 80 + url.szHost = LCase(sz) +Else + df = Val(Mid(sz, n + 1)) + url.nPort = IIf(df < 0.5 Or df >= 65535.5, 80, sz) + url.szHost = LCase(Left(sz, n - 1)) +End If + +End Sub + +Public Function UrlString(ByRef url As UrlType) As String + +UrlString = url.szProto + "://" + url.szHost + IIf(url.szProto = "http" And url.nPort = 80, "", ":" + LTrim(Str(url.nPort))) + "/" + url.szFile + "#" + url.szLabel + +End Function + +' === End URLs === + +' === Begin MsgBoxWithIcon/MsgBoxMute === + +Public Sub MsgBoxWithIcon(ByVal szPrompt As String, Optional ByVal szTitle As String = vbNullString, Optional ByVal szIconFile As String = vbNullString, Optional ByVal nIcon As Integer = 1) + +Dim mbp As MsgBoxParams +Dim hInstance As Long, hIcon As Long + +If szIconFile = vbNullString Then + hInstance = App.hInstance +Else + hInstance = LoadLibraryA(szIconFile) +End If +If hInstance = 0 Then + hIcon = 0 +Else + hIcon = LoadIconA(hInstance, 1) +End If +If hIcon = 0 Then + MsgBox szPrompt, vbInformation, szTitle +Else + MessageBeep MB_ICONINFORMATION + mbp.cbSize = Len(mbp) + mbp.hwndOwner = 0 + On Error Resume Next + mbp.hwndOwner = Screen.ActiveForm.hWnd + On Error GoTo 0 + mbp.hInstance = hInstance + mbp.lpszText = szPrompt + mbp.lpszCaption = IIf(szTitle = vbNullString, App.Title, szTitle) + mbp.dwStyle = MB_USERICON + mbp.lpszIcon = nIcon + mbp.dwContextHelpId = 0 + mbp.lpfnMsgBoxCallback = 0 + mbp.dwLanguageId = 0 + MessageBoxIndirectA mbp +End If +If hInstance <> 0 And hInstance <> App.hInstance Then + FreeLibrary mbp.hInstance +End If + +End Sub + +Public Sub MsgBoxMute(ByVal szPrompt As String, Optional ByVal dwStyle As Long = 0, Optional ByVal szTitle As String = vbNullString) + +Dim mbp As MsgBoxParams +mbp.cbSize = Len(mbp) +mbp.hwndOwner = 0 +On Error Resume Next +mbp.hwndOwner = Screen.ActiveForm.hWnd +On Error GoTo 0 +mbp.hInstance = 0 +mbp.lpszText = szPrompt +mbp.lpszCaption = IIf(szTitle = vbNullString, App.Title, szTitle) +mbp.dwStyle = MB_USERICON +mbp.lpszIcon = dwStyle \ 16 + IDI_APPLICATION +mbp.dwContextHelpId = 0 +mbp.lpfnMsgBoxCallback = 0 +mbp.dwLanguageId = 0 +' Unsupported under Windows 98 +If MessageBoxIndirectA(mbp) = 0 Then + mbp.hInstance = App.hInstance + mbp.lpszIcon = 1 + MessageBoxIndirectA mbp +End If + +End Sub + +' === End MsgBoxWithIcon/MsgBoxMute === + +' === Begin Open/Save === + +Private Function TrimNull(ByVal sz As String) As String + +Dim i As Integer +i = InStr(sz, Chr(0)) +If i > 0 Then + TrimNull = Left(sz, i - 1) +Else + TrimNull = sz +End If + +End Function + +Public Function OpenFileDialog(ByVal szTitle As String, ByVal szFilter As String) As String + +Dim ofn As OpenFileName +szFilter = Replace(szFilter, "|", Chr(0)) +ofn.lStructSize = Len(ofn) +ofn.hwndOwner = 0 +On Error Resume Next +ofn.hwndOwner = Screen.ActiveForm.hWnd +On Error GoTo 0 +ofn.lpstrCustomFilter = vbNullString +ofn.lpstrFileTitle = vbNullString +ofn.lpstrInitialDir = vbNullString +ofn.lpstrFilter = szFilter + Chr(0) +ofn.lpstrFile = String(260, 0) +ofn.nMaxFile = 260 +ofn.lpstrTitle = szTitle +ofn.flags = OFN_PATHMUSTEXIST + OFN_FILEMUSTEXIST + OFN_HIDEREADONLY +ofn.nFileOffset = 0 +ofn.nFileExtension = 0 +ofn.lpstrDefExt = vbNullString +ofn.lCustData = 0 +If GetOpenFileNameA(ofn) <> 0 Then + OpenFileDialog = TrimNull(ofn.lpstrFile) +Else + OpenFileDialog = "" +End If + +End Function + +Public Function SaveFileDialog(ByVal szTitle As String, ByVal szFilter As String, ByVal szFileName As String) As String + +Dim ofn As OpenFileName, sz As String +szFilter = Replace(szFilter, "|", Chr(0)) +ofn.lStructSize = Len(ofn) +ofn.hwndOwner = 0 +On Error Resume Next +ofn.hwndOwner = Screen.ActiveForm.hWnd +On Error GoTo 0 +ofn.lpstrCustomFilter = vbNullString +ofn.lpstrFileTitle = vbNullString +ofn.lpstrInitialDir = vbNullString +ofn.lpstrFilter = szFilter + Chr(0) +ofn.lpstrFile = szFileName + String(260, 0) +ofn.nMaxFile = 260 +ofn.lpstrTitle = szTitle +ofn.flags = OFN_PATHMUSTEXIST + OFN_HIDEREADONLY + OFN_OVERWRITEPROMPT +ofn.nFileOffset = 0 +ofn.nFileExtension = 0 +ofn.lpstrDefExt = vbNullString +ofn.lCustData = 0 +If GetSaveFileNameA(ofn) <> 0 Then + SaveFileDialog = TrimNull(ofn.lpstrFile) +Else + SaveFileDialog = "" +End If + +End Function + +' === End OpenFile === + +' === Begin BrowseForFolder === + +Public Function BrowseForFolder(ByVal szTitle As String) As String + +Dim bi As BrowseInfo +Dim szDisplayName(0 To 259) As Byte +Dim pidl As Long + +bi.hwndOwner = 0 +On Error Resume Next +bi.hwndOwner = Screen.ActiveForm.hWnd +On Error GoTo 0 +bi.pidlRoot = 0 +bi.pszDisplayName = 0 +bi.lpszTitle = szTitle +bi.ulFlags = BIF_RETURNONLYFSDIRS +bi.lpfn = 0 +bi.lParam = 0 +bi.iImage = 0 +pidl = SHBrowseForFolderA(bi) +If SHGetPathFromIDListA(pidl, VarPtr(szDisplayName(0))) = 0 Then + BrowseForFolder = "" +Else + BrowseForFolder = MkBStr(VarPtr(szDisplayName(0))) +End If + +End Function + +' === End BrowseForFolder === + +' === Begin TrayIcon === + +Public Sub AddTrayIcon(ByRef frm As Form, Optional ByVal hIcon As Long = 0, Optional ByVal szTip As String = "") + +Dim nid As NotifyIconData +nid.cbSize = Len(nid) +nid.hWnd = frm.hWnd +nid.uID = 0 +nid.uFlags = NIF_MESSAGE + NIF_ICON + NIF_TIP +nid.uCallbackMessage = WM_TRAY +nid.hIcon = IIf(hIcon = 0, frm.Icon.Handle, hIcon) +nid.szTip = IIf(szTip = "", frm.Caption, szTip) + Chr(0) +Shell_NotifyIcon NIM_ADD, nid + +End Sub + +Public Sub DeleteTrayIcon(ByRef frm As Form) + +Dim nid As NotifyIconData +nid.cbSize = Len(nid) +nid.hWnd = frm.hWnd +nid.uID = 0 +nid.uFlags = 0 +Shell_NotifyIcon NIM_DELETE, nid + +End Sub + +' === End TrayIcon === + +' === Begin Stdio === + +Public Sub StdioOpen() + +GetStartupInfoA Stdio_si +If (Stdio_si.dwFlags And STARTF_USESTDHANDLES) = 0 Then + AllocConsole + Stdio_si.hStdInput = GetStdHandle(STD_INPUT_HANDLE) + Stdio_si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE) + Stdio_si.hStdError = GetStdHandle(STD_ERROR_HANDLE) +End If + +End Sub + +Public Sub StdioClose() + +If (Stdio_si.dwFlags And STARTF_USESTDHANDLES) = 0 Then + FreeConsole +End If + +End Sub + +Public Function StdioIn() As String + +Dim nNumberOfBytesRead As Long +Dim lpBuffer(0 To READ_BUFFER_LEN - 1) As Byte +ReadFile Stdio_si.hStdInput, VarPtr(lpBuffer(0)), READ_BUFFER_LEN, nNumberOfBytesRead +StdioIn = SysAllocStringByteLen(VarPtr(lpBuffer(0)), nNumberOfBytesRead) + +End Function + +Public Sub StdioOut(ByVal sz As String) + +Dim nNumberOfBytesWritten As Long +WriteFile Stdio_si.hStdOutput, CvBStr(sz), lstrlenA(sz), nNumberOfBytesWritten + +End Sub + +Public Sub StdioErr(ByVal sz As String) + +Dim nNumberOfBytesWritten As Long +WriteFile Stdio_si.hStdError, CvBStr(sz), lstrlenA(sz), nNumberOfBytesWritten + +End Sub + +' === End Stdio === + diff --git a/base/MAKEFILE.BAT b/base/MAKEFILE.BAT new file mode 100644 index 0000000..a51eb99 --- /dev/null +++ b/base/MAKEFILE.BAT @@ -0,0 +1,7 @@ +@ECHO OFF +CL /O2 /W3 /LD /DPIPE_DLL /Fe..\BIN\PIPE.DLL PIPE.CPP +CL /O2 /W3 /LD /DWSOCKBAS_DLL /Fe..\BIN\WSOCKBAS.DLL WSOCKBAS.CPP WSOCK32.LIB +CL /O2 /W3 /LD /Fe..\BIN\ALPHABLT.DLL ALPHABLT.C GDI32.LIB +DEL *.OBJ +DEL ..\BIN\*.LIB +DEL ..\BIN\*.EXP \ No newline at end of file diff --git a/base/PIPE.BAS b/base/PIPE.BAS new file mode 100644 index 0000000..1b145e6 --- /dev/null +++ b/base/PIPE.BAS @@ -0,0 +1,16 @@ +Attribute VB_Name = "mdlPipe" +Option Explicit + +Public Type PipeStruct + hInput As Long + hOutput As Long + bConsole As Long + nBytesLeft As Long + nReadEnd As Long + szBuffer(0 To 4095) As Byte +End Type + +Public Declare Sub PipeOpen Lib "PIPE.DLL" Alias "_PipeOpen@8" (ByRef pipe As PipeStruct, Optional ByVal szProcFile As String = vbNullString) +Public Declare Sub PipeClose Lib "PIPE.DLL" Alias "_PipeClose@4" (ByRef pipe As PipeStruct) +Public Declare Function PipeLineInput Lib "PIPE.DLL" Alias "_PipeLineInput@4" (ByRef pipe As PipeStruct) As Long +Public Declare Sub PipeLineOutput Lib "PIPE.DLL" Alias "_PipeLineOutput@8" (ByRef pipe As PipeStruct, ByVal szLineStr As String) diff --git a/base/WSOCKBAS.BAS b/base/WSOCKBAS.BAS new file mode 100644 index 0000000..1429bae --- /dev/null +++ b/base/WSOCKBAS.BAS @@ -0,0 +1,14 @@ +Attribute VB_Name = "mdlWinSockBasic" +Option Explicit + +Public Const INVALID_SOCKET As Long = -1 + +Public Declare Sub WSBStartup Lib "WSOCKBAS.DLL" Alias "_WSBStartup@0" () +Public Declare Sub WSBCleanup Lib "WSOCKBAS.DLL" Alias "_WSBCleanup@0" () +Public Declare Function WSBOpenServer Lib "WSOCKBAS.DLL" Alias "_WSBOpenServer@4" (ByVal nPort As Long) As Long +Public Declare Sub WSBCloseServer Lib "WSOCKBAS.DLL" Alias "_WSBCloseServer@4" (ByVal nSocket As Long) +Public Declare Function WSBAccept Lib "WSOCKBAS.DLL" Alias "_WSBAccept@4" (ByVal nSocket As Long) As Long +Public Declare Function WSBConnect Lib "WSOCKBAS.DLL" Alias "_WSBConnect@8" (ByVal lpszHost As String, ByVal nPort As Long) As Long +Public Declare Sub WSBDisconnect Lib "WSOCKBAS.DLL" Alias "_WSBDisconnect@4" (ByVal nSocket As Long) +Public Declare Function WSBRecv Lib "WSOCKBAS.DLL" Alias "_WSBRecv@12" (ByVal nSocket As Long, ByVal lpBuffer As Long, ByVal nLen As Long) As Long +Public Declare Function WSBSend Lib "WSOCKBAS.DLL" Alias "_WSBSend@12" (ByVal nSocket As Long, ByVal lpBuffer As Long, ByVal nLen As Long) As Long diff --git a/base/base.h b/base/base.h new file mode 100644 index 0000000..a0bbe6a --- /dev/null +++ b/base/base.h @@ -0,0 +1,151 @@ +#include +#include + +#ifndef BASE_H +#define BASE_H + +#ifdef _MSC_VER + typedef signed __int64 int64_t; // ll + typedef unsigned __int64 uint64_t; // qw + typedef signed __int32 int32_t; // l + typedef unsigned __int32 uint32_t; // dw + typedef signed __int16 int16_t; // s + typedef unsigned __int16 uint16_t; // w + typedef signed __int8 int8_t; // c + typedef unsigned __int8 uint8_t; // uc + #define FORMAT_I64 "I64" +#else + #include + #define FORMAT_I64 "ll" +#endif + +#define __ASSERT(a) assert(a) +#define __ASSERT_BOUND(a, b, c) assert((a) <= (b) && (b) <= (c)) +#define __ASSERT_BOUND_2(a, b, c, d) assert((a) <= (b) && (b) <= (c) && (c) <= (d)) + +inline bool EQV(bool bArg1, bool bArg2) { + return bArg1 ? bArg2 : !bArg2; +} + +inline bool XOR(bool bArg1, bool bArg2) { + return bArg1 ? !bArg2 : bArg2; +} + +template inline T MIN(T Arg1, T Arg2) { + return Arg1 < Arg2 ? Arg1 : Arg2; +} + +template inline T MAX(T Arg1, T Arg2) { + return Arg1 > Arg2 ? Arg1 : Arg2; +} + +template inline T ABS(T Arg) { + return Arg < 0 ? -Arg : Arg; +} + +template inline T SQR(T Arg) { + return Arg * Arg; +} + +template inline void SWAP(T &Arg1, T &Arg2) { + T Temp; + Temp = Arg1; + Arg1 = Arg2; + Arg2 = Temp; +} + +inline int PopCnt8(uint8_t uc) { + int n; + n = ((uc >> 1) & 0x55) + (uc & 0x55); + n = ((n >> 2) & 0x33) + (n & 0x33); + return (n >> 4) + (n & 0x0f); +} + +inline int PopCnt16(uint16_t w) { + int n; + n = ((w >> 1) & 0x5555) + (w & 0x5555); + n = ((n >> 2) & 0x3333) + (n & 0x3333); + n = ((n >> 4) & 0x0f0f) + (n & 0x0f0f); + return (n >> 8) + (n & 0x00ff); +} + +inline int PopCnt32(uint32_t dw) { + int n; + n = ((dw >> 1) & 0x55555555) + (dw & 0x55555555); + n = ((n >> 2) & 0x33333333) + (n & 0x33333333); + n = ((n >> 4) & 0x0f0f0f0f) + (n & 0x0f0f0f0f); + n = ((n >> 8) & 0x00ff00ff) + (n & 0x00ff00ff); + return (n >> 16) + (n & 0x0000ffff); +} + +inline int64_t GetTime() { + timeb tb; + ftime(&tb); + return (int64_t) tb.time * 1000 + tb.millitm; +} + +template class Stack { + int nSize, nTop; + T *Elements; + +public: + Stack(int nSize_) { + nSize = nSize_; + nTop = 0; + Elements = new T[nSize]; + } + ~Stack(void) { + delete[] Elements; + } + bool Push(T o) { + if (nTop == nSize) { + return false; + } + Elements[nTop] = o; + nTop ++; + return true; + } + bool Pop(T &o) { + if (nTop == 0) { + return false; + } + nTop --; + o = Elements[nTop]; + return true; + } +}; + +template class Queue { + int nSize, nHead, nTail; + T *Elements; + +public: + Queue(int nSize_) { + nSize = nSize_; + nHead = nTail = 0; + Elements = new T[nSize]; + } + ~Queue(void) { + delete[] Elements; + } + bool Offer(T o) { + int nNewTail = (nTail + 1) % nSize; + if (nNewTail == nHead) { + return false; + } + Elements[nTail] = o; + nTail = nNewTail; + return true; + } + bool Poll(T &o) { + if (nTail == nHead) { + return false; + } + int nOldHead = nHead; + nHead = (nHead + 1) % nSize; + o = Elements[nOldHead]; + return true; + } +}; + +#endif diff --git a/base/base2.h b/base/base2.h new file mode 100644 index 0000000..ca28de2 --- /dev/null +++ b/base/base2.h @@ -0,0 +1,78 @@ +#ifdef _WIN32 + #include +#else + #include + #include + #include +#endif +#include +#include "base.h" + +#ifndef BASE2_H +#define BASE2_H + +const int PATH_MAX_CHAR = 1024; + +#ifdef _WIN32 + +inline void Idle(void) { + Sleep(1); +} + +const int PATH_SEPERATOR = '\\'; + +inline bool AbsolutePath(const char *sz) { + return sz[0] == '\\' || (((sz[0] >= 'A' && sz[0] <= 'Z') || (sz[0] >= 'a' && sz[0] <= 'z')) && sz[1] == ':'); +} + +inline void GetSelfExe(char *szDst) { + GetModuleFileName(NULL, szDst, PATH_MAX_CHAR); +} + +inline void StartThread(void *ThreadEntry(void *), void *lpParameter) { + DWORD dwThreadId; + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) ThreadEntry, (LPVOID) lpParameter, 0, &dwThreadId); +} + +#else + +inline void Idle(void) { + usleep(1000); +} + +const int PATH_SEPERATOR = '/'; + +inline bool AbsolutePath(const char *sz) { + return sz[0] == '/' || (sz[0] == '~' && sz[1] == '/'); +} + +inline void GetSelfExe(char *szDst) { + readlink("/proc/self/exe", szDst, PATH_MAX_CHAR); +} + +inline void StartThread(void *ThreadEntry(void *), void *lpParameter) { + pthread_t pthread; + pthread_attr_t pthread_attr; + pthread_attr_init(&pthread_attr); + pthread_attr_setscope(&pthread_attr, PTHREAD_SCOPE_SYSTEM); + pthread_create(&pthread, &pthread_attr, ThreadEntry, lpParameter); +} + +#endif + +inline void LocatePath(char *szDst, const char *szSrc) { + char *lpSeperator; + if (AbsolutePath(szSrc)) { + strcpy(szDst, szSrc); + } else { + GetSelfExe(szDst); + lpSeperator = strrchr(szDst, PATH_SEPERATOR); + if (lpSeperator == NULL) { + strcpy(szDst, szSrc); + } else { + strcpy(lpSeperator + 1, szSrc); + } + } +} + +#endif diff --git a/base/parse.h b/base/parse.h new file mode 100644 index 0000000..8e1a71d --- /dev/null +++ b/base/parse.h @@ -0,0 +1,131 @@ +#include +#include + +#ifndef PARSE_H +#define PARSE_H + +#ifdef _WIN32 + +#include +#include + +inline char *strcasestr(const char *sz1, const char *sz2) { + return StrStrI(sz1, sz2); +} + +#endif + +#ifdef _MSC_VER + inline int strncasecmp(const char *sz1, const char *sz2, size_t n) { + return strnicmp(sz1, sz2, n); + } +#endif + +inline void StrCutCrLf(char *sz) { + char *lpsz; + lpsz = strchr(sz, '\r'); + if (lpsz != NULL) { + *lpsz = '\0'; + } + lpsz = strchr(sz, '\n'); + if (lpsz != NULL) { + *lpsz = '\0'; + } +} + +inline bool StrEqv(const char *sz1, const char *sz2) { + return strncasecmp(sz1, sz2, strlen(sz2)) == 0; +} + +inline bool StrEqvSkip(const char *&sz1, const char *sz2) { + if (strncasecmp(sz1, sz2, strlen(sz2)) == 0) { + sz1 += strlen(sz2); + return true; + } else { + return false; + } +} + +inline bool StrEqvSkip(char *&sz1, const char *sz2) { + if (strncasecmp(sz1, sz2, strlen(sz2)) == 0) { + sz1 += strlen(sz2); + return true; + } else { + return false; + } +} + +inline bool StrScan(const char *sz1, const char *sz2) { + return strcasestr(sz1, sz2) != NULL; +} + +inline bool StrScanSkip(const char *&sz1, const char *sz2) { + const char *lpsz; + lpsz = strcasestr(sz1, sz2); + if (lpsz == NULL) { + return false; + } else { + sz1 = lpsz + strlen(sz2); + return true; + } +} + +inline bool StrScanSkip(char *&sz1, const char *sz2) { + char *lpsz; + lpsz = strcasestr(sz1, sz2); + if (lpsz == NULL) { + return false; + } else { + sz1 = lpsz + strlen(sz2); + return true; + } +} + +inline bool StrSplitSkip(const char *&szSrc, int nSeperator, char *szDst = NULL) { + const char *lpsz; + lpsz = strchr(szSrc, nSeperator); + if (lpsz == NULL) { + if (szDst != NULL) { + strcpy(szDst, szSrc); + } + szSrc += strlen(szSrc); + return false; + } else { + if (szDst != NULL) { + strncpy(szDst, szSrc, lpsz - szSrc); + szDst[lpsz - szSrc] = '\0'; + } + szSrc = lpsz + 1; + return true; + } +} + +inline bool StrSplitSkip(char *&szSrc, int nSeperator, char *szDst = NULL) { + char *lpsz; + lpsz = strchr(szSrc, nSeperator); + if (lpsz == NULL) { + if (szDst != NULL) { + strcpy(szDst, szSrc); + } + szSrc += strlen(szSrc); + return false; + } else { + if (szDst != NULL) { + strncpy(szDst, szSrc, lpsz - szSrc); + szDst[lpsz - szSrc] = '\0'; + } + szSrc = lpsz + 1; + return true; + } +} + +inline int Str2Digit(const char *sz, int nMin, int nMax) { + int nRet; + if (sscanf(sz, "%d", &nRet) > 0) { + return MIN(MAX(nRet, nMin), nMax); + } else { + return nMin; + } +} + +#endif diff --git a/base/pipe.cpp b/base/pipe.cpp new file mode 100644 index 0000000..95b56a9 --- /dev/null +++ b/base/pipe.cpp @@ -0,0 +1,257 @@ +#include +#ifdef _WIN32 + #include +#else + #include +#endif +#include "base2.h" +#include "pipe.h" + +inline void ParsePath(char *szDir, char *szFile, const char *szPath) { + char *lpSeperator; + strcpy(szDir, szPath); + lpSeperator = strrchr(szDir, PATH_SEPERATOR); + if (lpSeperator == NULL) { + szDir[0] = '\0'; + strcpy(szFile, szPath); + } else { + *lpSeperator = '\0'; + strcpy(szFile, lpSeperator + 1); + } +} + +#ifdef _WIN32 + +#ifdef PIPE_DLL + +extern "C" __declspec(dllexport) VOID WINAPI PipeOpen(PipeStruct *lppipe, LPCSTR szProcFile); +extern "C" __declspec(dllexport) VOID WINAPI PipeClose(PipeStruct *lppipe); +extern "C" __declspec(dllexport) LPSTR WINAPI PipeLineInput(PipeStruct *lppipe); +extern "C" __declspec(dllexport) VOID WINAPI PipeLineOutput(PipeStruct *lppipe, LPCSTR szLineStr); + +VOID WINAPI PipeOpen(PipeStruct *lppipe, LPCSTR szProcFile) { + lppipe->Open(szProcFile); +} + +VOID WINAPI PipeClose(PipeStruct *lppipe) { + lppipe->Close(); +} + +LPSTR WINAPI PipeLineInput(PipeStruct *lppipe) { + static char szBuffer[LINE_INPUT_MAX_CHAR]; + if (lppipe->LineInput(szBuffer)) { + return szBuffer; + } else { + return NULL; + } +} + +VOID WINAPI PipeLineOutput(PipeStruct *lppipe, LPCSTR szLineStr) { + lppipe->LineOutput(szLineStr); +} + +#endif + +void PipeStruct::Open(const char *szProcFile) { + DWORD dwMode; + HANDLE hStdinRead, hStdinWrite, hStdoutRead, hStdoutWrite; + SECURITY_ATTRIBUTES sa; + STARTUPINFO si; + PROCESS_INFORMATION pi; + char szFileDir[PATH_MAX_CHAR], szFileName[PATH_MAX_CHAR], szCurDir[PATH_MAX_CHAR]; + + if (szProcFile == NULL) { + hInput = GetStdHandle(STD_INPUT_HANDLE); + hOutput = GetStdHandle(STD_OUTPUT_HANDLE); + bConsole = GetConsoleMode(hInput, &dwMode); + } else { + GetCurrentDirectory(PATH_MAX_CHAR, szCurDir); + ParsePath(szFileDir, szFileName, szProcFile); + SetCurrentDirectory(szFileDir); + + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + CreatePipe(&hStdinRead, &hStdinWrite, &sa, 0); + CreatePipe(&hStdoutRead, &hStdoutWrite, &sa, 0); + si.cb = sizeof(STARTUPINFO); + si.lpReserved = si.lpDesktop = si.lpTitle = NULL; + si.dwFlags = STARTF_USESTDHANDLES; + si.cbReserved2 = 0; + si.lpReserved2 = NULL; + si.hStdInput = hStdinRead; + si.hStdOutput = hStdoutWrite; + si.hStdError = hStdoutWrite; + CreateProcess(NULL, (LPSTR) szFileName, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | IDLE_PRIORITY_CLASS, NULL, NULL, &si, &pi); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + CloseHandle(hStdinRead); + CloseHandle(hStdoutWrite); + hInput = hStdoutRead; + hOutput = hStdinWrite; + bConsole = FALSE; + + SetCurrentDirectory(szCurDir); + } + if (bConsole) { + SetConsoleMode(hInput, dwMode & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT)); + FlushConsoleInputBuffer(hInput); + } + nBytesLeft = 0; + nReadEnd = 0; +} + +void PipeStruct::Close(void) const { + CloseHandle(hInput); + CloseHandle(hOutput); +} + +void PipeStruct::ReadInput(void) { + DWORD dwBytes; + ReadFile(hInput, szBuffer + nReadEnd, LINE_INPUT_MAX_CHAR - nReadEnd, &dwBytes, NULL); + nReadEnd += dwBytes; + if (nBytesLeft > 0) { + nBytesLeft -= dwBytes; + } +} + +bool PipeStruct::CheckInput(void) { + DWORD dwEvents, dwBytes; + if (bConsole) { // a tty, or an un-redirected handle + GetNumberOfConsoleInputEvents(hInput, &dwEvents); + return dwEvents > 1; + } else if (nBytesLeft > 0) { // a pipe with remainder data + return true; + } else if (PeekNamedPipe(hInput, NULL, 0, NULL, &dwBytes, NULL)) { // a pipe without remainder data + nBytesLeft = dwBytes; + return nBytesLeft > 0; + } else { // a file, always true + return true; + } +} + +void PipeStruct::LineOutput(const char *szLineStr) const { + DWORD dwBytes; + int nStrLen; + char szWriteBuffer[LINE_INPUT_MAX_CHAR]; + nStrLen = strlen(szLineStr); + memcpy(szWriteBuffer, szLineStr, nStrLen); + szWriteBuffer[nStrLen] = '\r'; + szWriteBuffer[nStrLen + 1] = '\n'; + WriteFile(hOutput, szWriteBuffer, nStrLen + 2, &dwBytes, NULL); +} + +#else + +#include +#include +#include +#include +#include + +void PipeStruct::Open(const char *szProcFile) { + int nStdinPipe[2], nStdoutPipe[2]; + char szFileDir[PATH_MAX_CHAR], szFileName[PATH_MAX_CHAR]; + if (szProcFile == NULL) { + nInput = STDIN_FILENO; + nOutput = STDOUT_FILENO; + } else { + pipe(nStdinPipe); + pipe(nStdoutPipe); + if (fork() == 0) { + close(nStdinPipe[1]); + close(nStdoutPipe[0]); + dup2(nStdinPipe[0], STDIN_FILENO); + close(nStdinPipe[0]); + dup2(nStdoutPipe[1], STDOUT_FILENO); + dup2(nStdoutPipe[1], STDERR_FILENO); + close(nStdoutPipe[1]); + + ParsePath(szFileDir, szFileName, szProcFile); + chdir(szFileDir); + + nice(20); + execl(szFileName, szFileName, NULL); + exit(EXIT_FAILURE); + } + close(nStdinPipe[0]); + close(nStdoutPipe[1]); + nInput = nStdoutPipe[0]; + nOutput = nStdinPipe[1]; + } + nReadEnd = 0; +} + +void PipeStruct::Close(void) const { + close(nInput); + close(nOutput); +} + +void PipeStruct::ReadInput(void) { + nReadEnd += read(nInput, szBuffer + nReadEnd, LINE_INPUT_MAX_CHAR - nReadEnd); +} + +bool PipeStruct::CheckInput(void) { + fd_set set; + timeval tv; + int val; + FD_ZERO(&set); + FD_SET(nInput, &set); + tv.tv_sec = 0; + tv.tv_usec = 0; + val = select(nInput + 1, &set, NULL, NULL, &tv); + return (val > 0 && FD_ISSET(nInput, &set) > 0); +} + +void PipeStruct::LineOutput(const char *szLineStr) const { + int nStrLen; + char szWriteBuffer[LINE_INPUT_MAX_CHAR]; + nStrLen = strlen(szLineStr); + memcpy(szWriteBuffer, szLineStr, nStrLen); + szWriteBuffer[nStrLen] = '\n'; + write(nOutput, szWriteBuffer, nStrLen + 1); +} + +#endif + +bool PipeStruct::GetBuffer(char *szLineStr) { + char *lpFeedEnd; + int nFeedEnd; + lpFeedEnd = (char *) memchr(szBuffer, '\n', nReadEnd); + if (lpFeedEnd == NULL) { + return false; + } else { + nFeedEnd = lpFeedEnd - szBuffer; + memcpy(szLineStr, szBuffer, nFeedEnd); + szLineStr[nFeedEnd] = '\0'; + nFeedEnd ++; + nReadEnd -= nFeedEnd; + memcpy(szBuffer, szBuffer + nFeedEnd, nReadEnd); + lpFeedEnd = (char *) strchr(szLineStr, '\r'); + if (lpFeedEnd != NULL) { + *lpFeedEnd = '\0'; + } + return true; + } +} + +bool PipeStruct::LineInput(char *szLineStr) { + if (GetBuffer(szLineStr)) { + return true; + } else if (CheckInput()) { + ReadInput(); + if (GetBuffer(szLineStr)) { + return true; + } else if (nReadEnd == LINE_INPUT_MAX_CHAR) { + memcpy(szLineStr, szBuffer, LINE_INPUT_MAX_CHAR - 1); + szLineStr[LINE_INPUT_MAX_CHAR - 1] = '\0'; + szBuffer[0] = szBuffer[LINE_INPUT_MAX_CHAR - 1]; + nReadEnd = 1; + return true; + } else { + return false; + } + } else { + return false; + } +} diff --git a/base/pipe.h b/base/pipe.h new file mode 100644 index 0000000..c9d8fca --- /dev/null +++ b/base/pipe.h @@ -0,0 +1,31 @@ +#ifdef _WIN32 + #include +#endif +#include "base.h" + +#ifndef PIPE_H +#define PIPE_H + +const int LINE_INPUT_MAX_CHAR = 4096; + +struct PipeStruct { +#ifdef _WIN32 + HANDLE hInput, hOutput; + BOOL bConsole; + int nBytesLeft; +#else + int nInput, nOutput; +#endif + int nReadEnd; + char szBuffer[LINE_INPUT_MAX_CHAR]; + + void Open(const char *szExecFile = NULL); + void Close(void) const; + void ReadInput(void); + bool CheckInput(void); + bool GetBuffer(char *szLineStr); + bool LineInput(char *szLineStr); + void LineOutput(const char *szLineStr) const; +}; // pipe + +#endif diff --git a/base/rc4prng.h b/base/rc4prng.h new file mode 100644 index 0000000..c4d3e25 --- /dev/null +++ b/base/rc4prng.h @@ -0,0 +1,61 @@ +#include "base.h" +#include "x86asm.h" + +#ifndef RC4PRNG_H +#define RC4PRNG_H + +struct RC4Struct { + uint8_t s[256]; + int x, y; + + void Init(void *lpKey, int nKeyLen) { + int i, j; + x = y = j = 0; + for (i = 0; i < 256; i ++) { + s[i] = i; + } + for (i = 0; i < 256; i ++) { + j = (j + s[i] + ((uint8_t *) lpKey)[i % nKeyLen]) & 255; + SWAP(s[i], s[j]); + } + } + + void InitZero(void) { + uint32_t dwKey; + dwKey = 0; + Init(&dwKey, 4); + } + + void InitRand(void) { + union { + uint32_t dw[2]; + uint64_t qw; + } Seed; + timeb tb; + ftime(&tb); + Seed.qw = TimeStampCounter(); + Seed.dw[1] ^= (uint32_t) GetTime(); + Init(&Seed, 8); + } + + uint8_t NextByte(void) { + x = (x + 1) & 255; + y = (y + s[x]) & 255; + SWAP(s[x], s[y]); + return s[(s[x] + s[y]) & 255]; + } + + uint32_t NextLong(void) { + union { + uint8_t uc[4]; + uint32_t dw; + } Ret; + Ret.uc[0] = NextByte(); + Ret.uc[1] = NextByte(); + Ret.uc[2] = NextByte(); + Ret.uc[3] = NextByte(); + return Ret.dw; + } +}; + +#endif diff --git a/base/wsockbas.cpp b/base/wsockbas.cpp new file mode 100644 index 0000000..6c63b5d --- /dev/null +++ b/base/wsockbas.cpp @@ -0,0 +1,223 @@ +#ifdef _WIN32 + +#include + +#ifdef WSOCKBAS_DLL + +extern "C" __declspec(dllexport) VOID WINAPI WSBStartup(VOID); +extern "C" __declspec(dllexport) VOID WINAPI WSBCleanup(VOID); +extern "C" __declspec(dllexport) LONG WINAPI WSBOpenServer(LONG nPort); +extern "C" __declspec(dllexport) VOID WINAPI WSBCloseServer(LONG nPort); +extern "C" __declspec(dllexport) LONG WINAPI WSBAccept(LONG nSocket); +extern "C" __declspec(dllexport) LONG WINAPI WSBConnect(LPCSTR lpszHost, LONG nPort); +extern "C" __declspec(dllexport) VOID WINAPI WSBDisconnect(LONG nSocket); +extern "C" __declspec(dllexport) LONG WINAPI WSBRecv(LONG nSocket, LPSTR lpBuffer, LONG nLen); +extern "C" __declspec(dllexport) LONG WINAPI WSBSend(LONG nSocket, LPCSTR lpBuffer, LONG nLen); + +#endif + +static u_long uNonBlock = 1; +static int nSockAddrLen = sizeof(sockaddr_in); + +VOID WINAPI WSBStartup(VOID) { + WSADATA wsaData; + WSAStartup(0x101, &wsaData); +} + +VOID WINAPI WSBCleanup(VOID) { + WSACleanup(); +} + +LONG WINAPI WSBOpenServer(LONG nPort) { + SOCKET s; + sockaddr_in sa; + s = socket(AF_INET, SOCK_STREAM, 0); + sa.sin_family = AF_INET; + sa.sin_port = htons((unsigned short) nPort); + sa.sin_addr.s_addr = INADDR_ANY; + if (bind(s, (sockaddr *) &sa, sizeof(sockaddr_in)) == 0) { + listen(s, SOMAXCONN); + ioctlsocket(s, FIONBIO, &uNonBlock); + return (LONG) s; + } else { + closesocket(s); + return INVALID_SOCKET; + } +} + +VOID WINAPI WSBCloseServer(LONG nSocket) { + closesocket((SOCKET) nSocket); +} + +LONG WINAPI WSBAccept(LONG nSocket) { + SOCKET s; + sockaddr_in sa; + s = accept((SOCKET) nSocket, (sockaddr *) &sa, &nSockAddrLen); + if (s != INVALID_SOCKET) { + ioctlsocket(s, FIONBIO, &uNonBlock); + } + return (LONG) s; +} + +LONG WINAPI WSBConnect(LPCSTR lpszHost, LONG nPort) { + SOCKET s; + sockaddr_in sa; + hostent *h; + sa.sin_family = AF_INET; + sa.sin_port = htons((unsigned short) nPort); + h = gethostbyname(lpszHost); + if (h == NULL) { + sa.sin_addr.s_addr = inet_addr(lpszHost); + } else { + sa.sin_addr = *(in_addr *) h->h_addr; + } + s = socket(AF_INET, SOCK_STREAM, 0); + if (connect(s, (sockaddr *) &sa, sizeof(sockaddr_in)) == 0) { + ioctlsocket(s, FIONBIO, &uNonBlock); + return (LONG) s; + } else { + closesocket(s); + return INVALID_SOCKET; + } +} + +VOID WINAPI WSBDisconnect(LONG nSocket) { + shutdown((SOCKET) nSocket, 2); + closesocket((SOCKET) nSocket); +} + +LONG WINAPI WSBRecv(LONG nSocket, LPSTR lpBuffer, LONG nLen) { + int n; + n = recv((SOCKET) nSocket, lpBuffer, nLen, 0); + if (n < 0) { + if (WSAGetLastError() == WSAEWOULDBLOCK) { + return 0; + } else { + return -1; + } + } else if (n == 0) { + return -1; + } else { + return n; + } +} + +LONG WINAPI WSBSend(LONG nSocket, LPCSTR lpBuffer, LONG nLen) { + int n; + n = send((SOCKET) nSocket, lpBuffer, nLen, 0); + if (n < 0) { + if (WSAGetLastError() == WSAEWOULDBLOCK) { + return 0; + } else { + return -1; + } + } else { + return n; + } +} + +#else + +#include +#include +#include +#include +#include +#include +#include +#include + +const int INVALID_SOCKET = -1; + +static u_long uNonBlock = 1; +static socklen_t nSockAddrLen = sizeof(sockaddr_in); + +int WSBOpenServer(int nPort) { + int s; + sockaddr_in sa; + s = socket(AF_INET, SOCK_STREAM, 0); + sa.sin_family = AF_INET; + sa.sin_port = htons((unsigned short) nPort); + sa.sin_addr.s_addr = INADDR_ANY; + if (bind(s, (sockaddr *) &sa, sizeof(sockaddr_in)) == 0) { + listen(s, SOMAXCONN); + ioctl(s, FIONBIO, &uNonBlock); + return s; + } else { + close(s); + return INVALID_SOCKET; + } +} + +void WSBCloseServer(int nSocket) { + close(nSocket); +} + +int WSBAccept(int nSocket) { + int s; + sockaddr_in sa; + s = accept(nSocket, (sockaddr *) &sa, &nSockAddrLen); + if (s != INVALID_SOCKET) { + ioctl(s, FIONBIO, &uNonBlock); + } + return s; +} + +int WSBConnect(const char *lpszHost, int nPort) { + int s; + sockaddr_in sa; + hostent *h; + sa.sin_family = AF_INET; + sa.sin_port = htons((unsigned short) nPort); + h = gethostbyname(lpszHost); + if (h == NULL) { + sa.sin_addr.s_addr = inet_addr(lpszHost); + } else { + sa.sin_addr = *(in_addr *) h->h_addr; + } + s = socket(AF_INET, SOCK_STREAM, 0); + if (connect(s, (sockaddr *) &sa, sizeof(sockaddr_in)) == 0) { + ioctl(s, FIONBIO, &uNonBlock); + return s; + } else { + close(s); + return INVALID_SOCKET; + } +} + +void WSBDisconnect(int nSocket) { + shutdown(nSocket, 2); + close(nSocket); +} + +int WSBRecv(int nSocket, char *lpBuffer, int nLen) { + int n; + n = recv(nSocket, lpBuffer, nLen, 0); + if (n < 0) { + if (errno == EWOULDBLOCK) { + return 0; + } else { + return -1; + } + } else if (n == 0) { + return -1; + } else { + return n; + } +} + +int WSBSend(int nSocket, const char *lpBuffer, int nLen) { + int n; + n = send(nSocket, lpBuffer, nLen, 0); + if (n < 0) { + if (errno == EWOULDBLOCK) { + return 0; + } else { + return -1; + } + } else { + return n; + } +} + +#endif diff --git a/base/wsockbas.h b/base/wsockbas.h new file mode 100644 index 0000000..3260be7 --- /dev/null +++ b/base/wsockbas.h @@ -0,0 +1,34 @@ +#ifndef WSOCKBAS_H +#define WSOCKBAS_H + +#ifdef _WIN32 + +#include + +VOID WINAPI WSBStartup(VOID); +VOID WINAPI WSBCleanup(VOID); +LONG WINAPI WSBOpenServer(LONG nPort); +VOID WINAPI WSBCloseServer(LONG nSocket); +LONG WINAPI WSBAccept(LONG nSocket); +LONG WINAPI WSBConnect(LPCSTR lpszHost, LONG nPort); +VOID WINAPI WSBDisconnect(LONG nSocket); +LONG WINAPI WSBRecv(LONG nSocket, LPSTR lpBuffer, LONG nLen); +LONG WINAPI WSBSend(LONG nSocket, LPCSTR lpBuffer, LONG nLen); + +#else + +const int INVALID_SOCKET = -1; + +inline void WSBStartup(void) {}; +inline void WSBCleanup(void) {}; +int WSBOpenServer(int nPort); +void WSBCloseServer(int nSocket); +int WSBAccept(int nSocket); +int WSBConnect(const char *lpszHost, int nPort); +void WSBDisconnect(int nSocket); +int WSBRecv(int nSocket, char *lpBuffer, int nLen); +int WSBSend(int nSocket, const char *lpBuffer, int nLen); + +#endif + +#endif diff --git a/base/x86asm.h b/base/x86asm.h new file mode 100644 index 0000000..858650c --- /dev/null +++ b/base/x86asm.h @@ -0,0 +1,299 @@ +#include "base.h" + +#ifndef X86ASM_H +#define X86ASM_H + +inline uint32_t LOW_LONG(uint64_t Operand) { + return (uint32_t) Operand; +} + +inline uint32_t HIGH_LONG(uint64_t Operand) { + return (uint32_t) (Operand >> 32); +} + +inline uint64_t MAKE_LONG_LONG(uint32_t LowLong, uint32_t HighLong) { + return (uint64_t) LowLong | ((uint64_t) HighLong << 32); +} + +#ifdef _MSC_VER + +#pragma warning(disable: 4035) + +__forceinline int Exchange(volatile int *Target, int Value) { + __asm { + mov ebx, Target; + mov eax, Value; + xchg [ebx], eax; + } +} + +__forceinline int CompareExchange(volatile int *Destination, int Exchange, int Comperand) { + __asm { + mov ebx, Destination; + mov edx, Exchange; + mov eax, Comperand; + cmpxchg [ebx], edx; + } +} + +__forceinline int ExchangeAdd(volatile int *Addend, int Increment) { + __asm { + mov ebx, Addend; + mov eax, Increment; + xadd [ebx], eax; + } +} + +__forceinline int Bsf(uint32_t Operand) { + __asm { + bsf eax, Operand; + } +} + +__forceinline int Bsr(uint32_t Operand) { + __asm { + bsr eax, Operand; + } +} + +__forceinline uint64_t TimeStampCounter(void) { + __asm { + rdtsc; + } +} + +__forceinline uint64_t LongMul(uint32_t Multiplier, uint32_t Multiplicand) { + __asm { + mov eax, Multiplier; + mul Multiplicand; + } +} + +__forceinline uint64_t LongSqr(uint32_t Multiplier) { + __asm { + mov eax, Multiplier; + mul Multiplier; + } +} + +__forceinline uint32_t LongDiv(uint64_t Dividend, uint32_t Divisor) { + __asm { + mov eax, dword ptr Dividend[0]; + mov edx, dword ptr Dividend[4]; + div Divisor; + } +} + +__forceinline uint32_t LongMod(uint64_t Dividend, uint32_t Divisor) { + __asm { + mov eax, dword ptr Dividend[0]; + mov edx, dword ptr Dividend[4]; + div Divisor; + mov eax, edx; + } +} + +__forceinline uint32_t LongMulDiv(uint32_t Multiplier, uint32_t Multiplicand, uint32_t Divisor) { + __asm { + mov eax, Multiplier; + mul Multiplicand; + div Divisor; + } +} + +__forceinline uint32_t LongMulMod(uint32_t Multiplier, uint32_t Multiplicand, uint32_t Divisor) { + __asm { + mov eax, Multiplier; + mul Multiplicand; + div Divisor; + mov eax, edx; + } +} + +__forceinline uint32_t Shld(uint32_t HighLong, uint32_t LowLong, uint32_t Count) { + __asm { + mov eax, HighLong; + mov edx, LowLong; + mov ecx, Count; + shld eax, edx, cl; + } +} + +__forceinline uint32_t Shrd(uint32_t LowLong, uint32_t HighLong, uint32_t Count) { + __asm { + mov eax, LowLong; + mov edx, HighLong; + mov ecx, Count; + shrd eax, edx, cl; + } +} + +#pragma warning(default: 4035) + +#else + +static __inline__ int Exchange(volatile int *Target, int Value) { + int eax, ebx; + asm __volatile__ ( + "xchgl %0, (%1)" "\n\t" + : "=a" (eax), "=b" (ebx) + : "0" (Value), "1" (Target) + ); + return eax; +} + +static __inline__ int CompareExchange(volatile int *Destination, int Exchange, int Comperand) { + int eax, ebx, edx; + asm __volatile__ ( + "cmpxchgl %2, (%1)" "\n\t" + : "=a" (eax), "=b" (ebx), "=d" (edx) + : "0" (Comperand), "1" (Destination), "2" (Exchange) + ); + return eax; +} + +static __inline__ int ExchangeAdd(volatile int *Addend, int Increment) { + int eax, ebx; + asm __volatile__ ( + "xaddl %0, (%1)" "\n\t" + : "=a" (eax), "=b" (ebx) + : "0" (Increment), "1" (Addend) + ); + return eax; +} + +static __inline__ int Bsf(uint32_t Operand) { + int eax; + asm __volatile__ ( + "bsfl %0, %0" "\n\t" + : "=a" (eax) + : "0" (Operand) + ); + return eax; +} + +static __inline__ int Bsr(uint32_t Operand) { + int eax; + asm __volatile__ ( + "bsrl %0, %0" "\n\t" + : "=a" (eax) + : "0" (Operand) + ); + return eax; +} + +static __inline__ uint64_t TimeStampCounter(void) { + uint32_t eax, edx; + asm __volatile__ ( + "rdtsc" "\n\t" + : "=a" (eax), "=d" (edx) + : + ); + return MAKE_LONG_LONG(eax, edx); +} + +static __inline__ uint64_t LongMul(uint32_t Multiplier, uint32_t Multiplicand) { + uint32_t eax, edx; + asm __volatile__ ( + "mull %1" "\n\t" + : "=a" (eax), "=d" (edx) + : "0" (Multiplier), "1" (Multiplicand) + ); + return MAKE_LONG_LONG(eax, edx); +} + +static __inline__ uint64_t LongSqr(uint32_t Multiplier) { + uint32_t eax, edx; + asm __volatile__ ( + "mull %1" "\n\t" + : "=a" (eax), "=d" (edx) + : "0" (Multiplier), "1" (Multiplier) + ); + return MAKE_LONG_LONG(eax, edx); +} + +static __inline__ uint32_t LongDiv(uint64_t Dividend, uint32_t Divisor) { + uint32_t eax, edx, dummy; + asm __volatile__ ( + "divl %2" "\n\t" + : "=a" (eax), "=d" (edx), "=g" (dummy) + : "0" (LOW_LONG(Dividend)), "1" (HIGH_LONG(Dividend)), "2" (Divisor) + ); + return eax; +} + +static __inline__ uint32_t LongMod(uint64_t Dividend, uint32_t Divisor) { + uint32_t eax, edx, dummy; + asm __volatile__ ( + "divl %2" "\n\t" + : "=a" (eax), "=d" (edx), "=g" (dummy) + : "0" (LOW_LONG(Dividend)), "1" (HIGH_LONG(Dividend)), "2" (Divisor) + ); + return edx; +} + +static __inline__ uint32_t LongMulDiv(uint32_t Multiplier, uint32_t Multiplicand, uint32_t Divisor) { + uint32_t eax, edx, dummy; + asm __volatile__ ( + "mull %1" "\n\t" + "divl %2" "\n\t" + : "=a" (eax), "=d" (edx), "=g" (dummy) + : "0" (Multiplier), "1" (Multiplicand), "2" (Divisor) + ); + return eax; +} + +static __inline__ uint32_t LongMulMod(uint32_t Multiplier, uint32_t Multiplicand, uint32_t Divisor) { + uint32_t eax, edx, dummy; + asm __volatile__ ( + "mull %1" "\n\t" + "divl %2" "\n\t" + : "=a" (eax), "=d" (edx), "=g" (dummy) + : "0" (Multiplier), "1" (Multiplicand), "2" (Divisor) + ); + return edx; +} + +static __inline uint32_t Shld(uint32_t High, uint32_t Low, uint32_t Count) { + uint32_t eax, edx, ecx; + asm __volatile__ ( + "shldl %%cl, %1, %0" "\n\t" + : "=a" (eax), "=d" (edx), "=c" (ecx) + : "0" (High), "1" (Low), "2" (Count) + ); + return eax; +} + +static __inline uint32_t Shrd(uint32_t Low, uint32_t High, uint32_t Count) { + uint32_t eax, edx, ecx; + asm __volatile__ ( + "shrdl %%cl, %1, %0" "\n\t" + : "=a" (eax), "=d" (edx), "=c" (ecx) + : "0" (Low), "1" (High), "2" (Count) + ); + return eax; +} + +#endif + +inline uint64_t LongShl(uint64_t Operand, uint32_t Count) { + if (Count < 32) { + return MAKE_LONG_LONG(LOW_LONG(Operand) << Count, Shld(HIGH_LONG(Operand), LOW_LONG(Operand), Count)); + } else if (Count < 64) { + return MAKE_LONG_LONG(0, LOW_LONG(Operand) << (Count - 32)); + } else { + return MAKE_LONG_LONG(0, 0); + } +} + +inline uint64_t LongShr(uint64_t Operand, uint32_t Count) { + if (Count < 32) { + return MAKE_LONG_LONG(Shrd(LOW_LONG(Operand), HIGH_LONG(Operand), Count), HIGH_LONG(Operand) >> Count); + } else if (Count < 64) { + return MAKE_LONG_LONG(HIGH_LONG(Operand) >> (Count - 32), 0); + } else { + return MAKE_LONG_LONG(0, 0); + } +} + +#endif diff --git a/cchess/ADDECCO.CPP b/cchess/ADDECCO.CPP new file mode 100644 index 0000000..d96ac1a --- /dev/null +++ b/cchess/ADDECCO.CPP @@ -0,0 +1,122 @@ +/* +Add ECCO - Adds ECCO Opening & Variation to PGN Files +Designed by Morning Yellow, Version: 3.14, Last Modified: Jun. 2008 +Copyright (C) 2004-2008 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include "../base/base2.h" +#include "../eleeye/pregen.h" +#include "../cchess/cchess.h" +#include "../cchess/ecco.h" +#include "../cchess/pgnfile.h" + +static void AddEcco(const char *szPgnFile, const EccoApiStruct &EccoApi) { + int i, nStatus; + uint32_t dwEccoIndex, dwFileMove[20]; + PgnFileStruct pgn; + PositionStruct pos; + + if (pgn.Read(szPgnFile, NO_ADVERT)) { + pos.FromFen(cszStartFen); + for (i = 1; i <= MIN(pgn.nMaxMove, 20); i ++) { + dwFileMove[i - 1] = Move2File(pgn.wmvMoveTable[i], pos); + TryMove(pos, nStatus, pgn.wmvMoveTable[i]); + } + if (pgn.nMaxMove < 20) { + dwFileMove[pgn.nMaxMove] = 0; + } + dwEccoIndex = EccoApi.EccoIndex((const char *) dwFileMove); + strcpy(pgn.szEcco, (const char *) &dwEccoIndex); + strcpy(pgn.szOpen, EccoApi.EccoOpening(dwEccoIndex)); + strcpy(pgn.szVar, EccoApi.EccoVariation(dwEccoIndex)); + pgn.Write(szPgnFile); + } +} + +static void SearchFolder(const char *szFolderPath, const EccoApiStruct &EccoApi); + +static void SearchFile(const char *szFilePath, const WIN32_FIND_DATA &wfd, const EccoApiStruct &EccoApi) { + int nPathLen; + if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + nPathLen = strlen(szFilePath) - 4; + if (nPathLen > 0 && strnicmp(szFilePath + nPathLen, ".PGN", 4) == 0) { + AddEcco(szFilePath, EccoApi); + } + } else { + if (strcmp(wfd.cFileName, ".") != 0 && strcmp(wfd.cFileName, "..") != 0) { + SearchFolder(szFilePath, EccoApi); + } + } +} + +static void SearchFolder(const char *szFolderPath, const EccoApiStruct &EccoApi) { + char szFilePath[1024]; + WIN32_FIND_DATA wfd; + HANDLE hFind; + char *lpFilePath; + + strcpy(szFilePath, szFolderPath); + lpFilePath = szFilePath + strlen(szFolderPath); + if (*(lpFilePath - 1) == '\\') { + lpFilePath --; + } + strcpy(lpFilePath, "\\*"); + lpFilePath ++; + hFind = FindFirstFile(szFilePath, &wfd); + if (hFind != INVALID_HANDLE_VALUE) { + strcpy(lpFilePath, wfd.cFileName); + SearchFile(szFilePath, wfd, EccoApi); + while (FindNextFile(hFind, &wfd)) { + strcpy(lpFilePath, wfd.cFileName); + SearchFile(szFilePath, wfd, EccoApi); + } + } + FindClose(hFind); +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { + char szPath[MAX_PATH], szLibEccoFile[MAX_PATH]; + EccoApiStruct EccoApi; + BROWSEINFO bi; + LPITEMIDLIST pidl; + + LocatePath(szLibEccoFile, cszLibEccoFile); + if (!EccoApi.Startup(szLibEccoFile)) { + MessageBox(NULL, "没有找到ECCO.DLL!", "ECCO开局信息整理工具", MB_ICONEXCLAMATION); + return 0; + } + bi.hwndOwner = NULL; + bi.pidlRoot = NULL; + bi.pszDisplayName = NULL; + bi.lpszTitle = "请选择可移植棋谱(*.PGN)所在的文件夹"; + bi.ulFlags = BIF_RETURNONLYFSDIRS; + bi.lpfn = NULL; + bi.lParam = NULL; + bi.iImage = 0; + pidl = SHBrowseForFolder(&bi); + if (SHGetPathFromIDList(pidl, szPath)) { + PreGenInit(); + ChineseInit(); + SearchFolder(szPath, EccoApi); + MessageBox(NULL, "全部棋谱已加入ECCO开局信息。", "ECCO开局信息整理工具", MB_ICONINFORMATION); + } + EccoApi.Shutdown(); + return 0; +} \ No newline at end of file diff --git a/cchess/CCHESS.BAS b/cchess/CCHESS.BAS new file mode 100644 index 0000000..67eb51b --- /dev/null +++ b/cchess/CCHESS.BAS @@ -0,0 +1,185 @@ +Attribute VB_Name = "mdlCChess" +' CCHESS.BAS - Source Code for ElephantEye, Additional Part +' +' ElephantEye - a Chinese Chess Program (UCCI Engine) +' Designed by Morning Yellow, Version: 3.1, Last Modified: Dec. 2007 +' Copyright (C) 2004-2007 www.elephantbase.net +' +' This library is free software; you can redistribute it and/or +' modify it under the terms of the GNU Lesser General Public +' License as published by the Free Software Foundation; either +' version 2.1 of the License, or (at your option) any later version. +' +' This library 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 +' Lesser General Public License for more details. +' +' You should have received a copy of the GNU Lesser General Public +' License along with this library; if not, write to the Free Software +' Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Option Explicit + +Public Type RollbackStruct + dwZobristKey As Long + dwZobristLock0 As Long + dwZobristLock1 As Long + vlWhiteValue As Long + vlBlackValue As Long + mvs As Long +End Type + +Public Type PositionStruct + sdPlayer As Long + ucpcSquares(0 To 255) As Byte + ucsqPieces(0 To 47) As Byte + dwZobristKey As Long + dwZobristLock0 As Long + dwZobristLock1 As Long + dwBitPiece As Long + wBitRanks(0 To 15) As Integer + wBitFiles(0 To 15) As Integer + vlWhiteValue As Long + vlBlackValue As Long + nMoveNum As Long + nDistance As Long + rbsList(0 To 255) As RollbackStruct + ucRepHash(0 To 1023) As Byte +End Type + +Public Declare Function CchessVersion Lib "CCHESS.DLL" Alias "_CchessVersion@0" () As Long +Public Declare Sub CchessInit Lib "CCHESS.DLL" Alias "_CchessInit@4" (Optional ByVal bTraditional As Long = 0) +Public Declare Sub CchessPromotion Lib "CCHESS.DLL" Alias "_CchessPromotion@4" (Optional ByVal bPromotion As Long = 0) +Public Declare Sub CchessAddPiece Lib "CCHESS.DLL" Alias "_CchessAddPiece@16" (ByRef pos As PositionStruct, ByVal sq As Long, ByVal pc As Long, Optional ByVal bDel As Long = 0) +Public Declare Function CchessCanPromote Lib "CCHESS.DLL" Alias "_CchessCanPromote@8" (ByRef pos As PositionStruct, ByVal sq As Long) As Long +Public Declare Function CchessTryMove Lib "CCHESS.DLL" Alias "_CchessTryMove@12" (ByRef pos As PositionStruct, ByRef nStatus As Long, ByVal mv As Long) As Long +Public Declare Sub CchessUndoMove Lib "CCHESS.DLL" Alias "_CchessUndoMove@4" (ByRef pos As PositionStruct) +Public Declare Function CchessTryNull Lib "CCHESS.DLL" Alias "_CchessTryNull@4" (ByRef pos As PositionStruct) As Long +Public Declare Sub CchessUndoNull Lib "CCHESS.DLL" Alias "_CchessUndoNull@4" (ByRef pos As PositionStruct) +Public Declare Function CchessGenMoves Lib "CCHESS.DLL" Alias "_CchessGenMoves@8" (ByRef pos As PositionStruct, ByRef lpmv As Long) As Long +Public Declare Sub CchessSetIrrev Lib "CCHESS.DLL" Alias "_CchessSetIrrev@4" (ByRef pos As PositionStruct) +Public Declare Sub CchessClearBoard Lib "CCHESS.DLL" Alias "_CchessClearBoard@4" (ByRef pos As PositionStruct) +Public Declare Sub CchessBoardMirror Lib "CCHESS.DLL" Alias "_CchessBoardMirror@4" (ByRef pos As PositionStruct) +Public Declare Sub CchessExchangeSide Lib "CCHESS.DLL" Alias "_CchessExchangeSide@4" (ByRef pos As PositionStruct) +Public Declare Sub CchessFlipBoard Lib "CCHESS.DLL" Alias "_CchessFlipBoard@4" (ByRef pos As PositionStruct) +Public Declare Function CchessBoardText Lib "CCHESS.DLL" Alias "_CchessBoardText@8" (ByRef pos As PositionStruct, Optional ByVal bAnsi As Long = 0) As Long +Public Declare Function CchessBoard2Fen Lib "CCHESS.DLL" Alias "_CchessBoard2Fen@4" (ByRef pos As PositionStruct) As Long +Public Declare Sub CchessFen2Board Lib "CCHESS.DLL" Alias "_CchessFen2Board@8" (ByRef pos As PositionStruct, ByVal szFen As String) +Public Declare Function CchessFenMirror Lib "CCHESS.DLL" Alias "_CchessFenMirror@4" (ByVal szFenSrc As String) As Long +Public Declare Function CchessFileMirror Lib "CCHESS.DLL" Alias "_CchessFileMirror@4" (ByVal dwFileStr As Long) As Long +Public Declare Function CchessChin2File Lib "CCHESS.DLL" Alias "_CchessChin2File@8" (ByVal qwChinStr As Currency) As Long +Public Declare Function CchessFile2Chin Lib "CCHESS.DLL" Alias "_CchessFile2Chin@8" (ByVal dwFileStr As Long, ByVal sd As Long) As Currency +Public Declare Function CchessFile2Move Lib "CCHESS.DLL" Alias "_CchessFile2Move@8" (ByVal dwFileStr As Long, ByRef pos As PositionStruct) As Long +Public Declare Function CchessMove2File Lib "CCHESS.DLL" Alias "_CchessMove2File@8" (ByVal mv As Long, ByRef pos As PositionStruct) As Long + +Public Declare Function EccoVersion Lib "ECCO.DLL" Alias "_EccoVersion@0" () As Long +Public Declare Function EccoInitOpenVar Lib "ECCO.DLL" Alias "_EccoInitOpenVar@4" (Optional ByVal bTraditional As Long = 0) As Long +Public Declare Function EccoOpening Lib "ECCO.DLL" Alias "_EccoOpening@4" (ByVal dwEccoIndex As Long) As Long +Public Declare Function EccoVariation Lib "ECCO.DLL" Alias "_EccoVariation@4" (ByVal dwEccoIndex As Long) As Long +Public Declare Function EccoIndex Lib "ECCO.DLL" Alias "_EccoIndex@4" (ByVal szFileMove As String) As Long + +Public Const MOVE_ILLEGAL As Long = 256 +Public Const MOVE_INCHECK As Long = 128 +Public Const MOVE_CONSECUTIVE As Long = 64 +Public Const MOVE_PERPETUAL_LOSS As Long = 32 +Public Const MOVE_PERPETUAL_WIN As Long = 16 +Public Const MOVE_PERPETUAL As Long = 8 +Public Const MOVE_MATE As Long = 4 +Public Const MOVE_CHECK As Long = 2 +Public Const MOVE_CAPTURE As Long = 1 + +Public Const CCHESS_TRADITIONAL As Long = 1 +Public Const CCHESS_DELETE_PIECE As Long = 1 +Public Const CCHESS_ANSI_TEXT As Long = 1 + +Public Const CCHESS_START_FEN As String = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w" + +Public Function PieceType(ByVal nArg As Integer) As Integer + +PieceType = Choose(nArg - 15, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 6, 6, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 6, 6) + +End Function + +Public Function PieceByte(ByVal nArg As Integer) As String + +PieceByte = Choose(nArg + 1, "K", "A", "B", "N", "R", "C", "P") + +End Function + +Public Function Src(ByVal mv As Long) As Integer + +Src = mv Mod 256 + +End Function + +Public Function Dst(ByVal mv As Long) As Integer + +Dst = mv \ 256 + +End Function + +Public Function Move(ByVal sqSrc As Integer, ByVal sqDst As Integer) As Long + +Move = sqSrc + sqDst * 256& + +End Function + +Public Function MoveMirror(ByVal mv As Long) As Long + +Dim sqSrc As Integer, sqDst As Integer +sqSrc = (Src(mv) \ 16) * 16 + 14 - Src(mv) Mod 16 +sqDst = (Dst(mv) \ 16) * 16 + 14 - Dst(mv) Mod 16 +If sqSrc >= 0 And sqSrc <= 255 And sqDst >= 0 And sqDst <= 255 Then + MoveMirror = Move(sqSrc, sqDst) +Else + MoveMirror = 0 +End If + +End Function + +Public Function Coord2Move(ByVal szCoord As String) As Long + +Dim sqSrc As Integer, sqDst As Integer +If Len(szCoord) < 4 Then + Coord2Move = 0 +Else + sqSrc = (60 - Asc(Mid(szCoord, 2, 1))) * 16 + Asc(Mid(szCoord, 1, 1)) - 94 + sqDst = (60 - Asc(Mid(szCoord, 4, 1))) * 16 + Asc(Mid(szCoord, 3, 1)) - 94 + If sqSrc >= 0 And sqSrc <= 255 And sqDst >= 0 And sqDst <= 255 Then + Coord2Move = Move(sqSrc, sqDst) + Else + Coord2Move = 0 + End If +End If + +End Function + +Public Function Move2Coord(ByVal mv As Long) As String + +Move2Coord = Chr(Src(mv) Mod 16 + 94) + Chr(60 - Src(mv) \ 16) + Chr(Dst(mv) Mod 16 + 94) + Chr(60 - Dst(mv) \ 16) + +End Function + +Public Function Iccs2Move(ByVal szIccs As String) As Long + +Dim sqSrc As Integer, sqDst As Integer +If Len(szIccs) < 5 Then + Iccs2Move = 0 +Else + sqSrc = (60 - Asc(Mid(szIccs, 2, 1))) * 16 + Asc(Mid(szIccs, 1, 1)) - 62 + sqDst = (60 - Asc(Mid(szIccs, 5, 1))) * 16 + Asc(Mid(szIccs, 4, 1)) - 62 + If sqSrc >= 0 And sqSrc <= 255 And sqDst >= 0 And sqDst <= 255 Then + Iccs2Move = Move(sqSrc, sqDst) + Else + Iccs2Move = 0 + End If +End If + +End Function + +Public Function Move2Iccs(ByVal mv As Long) As String + +Move2Iccs = Chr(Src(mv) Mod 16 + 62) + Chr(60 - Src(mv) \ 16) + "-" + Chr(Dst(mv) Mod 16 + 62) + Chr(60 - Dst(mv) \ 16) + +End Function diff --git a/cchess/MAKEFILE.BAT b/cchess/MAKEFILE.BAT new file mode 100644 index 0000000..649cf04 --- /dev/null +++ b/cchess/MAKEFILE.BAT @@ -0,0 +1,8 @@ +@ECHO OFF +RC ..\RES\CCHESS.RC +CL /DNDEBUG /O2 /W3 /LD /DCCHESS_DLL /Fe..\BIN\CCHESS.DLL ..\ELEEYE\PREGEN.CPP ..\ELEEYE\POSITION.CPP ..\ELEEYE\GENMOVES.CPP CCHESS.CPP ..\RES\CCHESS.RES +CL /DNDEBUG /O2 /W3 /Fe..\BIN\ADDECCO.EXE ..\ELEEYE\PREGEN.CPP ..\ELEEYE\POSITION.CPP ..\ELEEYE\GENMOVES.CPP ..\CCHESS\CCHESS.CPP ..\CCHESS\PGNFILE.CPP ADDECCO.CPP USER32.LIB SHLWAPI.LIB SHELL32.LIB +DEL ..\RES\CCHESS.RES +DEL *.OBJ +DEL ..\BIN\CCHESS.LIB +DEL ..\BIN\CCHESS.EXP diff --git a/cchess/cchess.cpp b/cchess/cchess.cpp new file mode 100644 index 0000000..5a5a0d1 --- /dev/null +++ b/cchess/cchess.cpp @@ -0,0 +1,1045 @@ +/* +cchess.h/cchess.cpp - Source Code for ElephantEye, Additional Part + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.13, Last Modified: Jun. 2008 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include "../base/base.h" +#include "../eleeye/position.h" +#include "cchess.h" + +/* 本程序是ElephantEye源程序的附加模块,作用是将ElephantEye的源程序应用到其他软件中。 + * 本程序的一个主要应用是中国象棋规则驱动程序,在编译时定义"CCHESS_DLL"后,即可编译成"CCHESS.DLL"。 + * 目前该驱动程序已经成为《象棋巫师》的一部分,这也使得《象棋巫师》在中国象棋规则处理上的核心代码公开化了。 + */ + +#ifdef CCHESS_DLL + +#include + +extern "C" __declspec(dllexport) LPCSTR WINAPI CchessVersion(VOID); +extern "C" __declspec(dllexport) VOID WINAPI CchessInit(BOOL bTraditional); +extern "C" __declspec(dllexport) VOID WINAPI CchessPromotion(BOOL bPromotion); +extern "C" __declspec(dllexport) VOID WINAPI CchessAddPiece(PositionStruct *lppos, LONG sq, LONG pc, BOOL bDel); +extern "C" __declspec(dllexport) BOOL WINAPI CchessCanPromote(PositionStruct *lppos, LONG sq); +extern "C" __declspec(dllexport) BOOL WINAPI CchessTryMove(PositionStruct *lppos, LPLONG lpStatus, LONG mv); +extern "C" __declspec(dllexport) VOID WINAPI CchessUndoMove(PositionStruct *lppos); +extern "C" __declspec(dllexport) BOOL WINAPI CchessTryNull(PositionStruct *lppos); +extern "C" __declspec(dllexport) VOID WINAPI CchessUndoNull(PositionStruct *lppos); +extern "C" __declspec(dllexport) LONG WINAPI CchessGenMoves(PositionStruct *lppos, LPLONG lpmv); +extern "C" __declspec(dllexport) VOID WINAPI CchessSetIrrev(PositionStruct *lppos); +extern "C" __declspec(dllexport) VOID WINAPI CchessClearBoard(PositionStruct *lppos); +extern "C" __declspec(dllexport) VOID WINAPI CchessBoardMirror(PositionStruct *lppos); +extern "C" __declspec(dllexport) VOID WINAPI CchessExchangeSide(PositionStruct *lppos); +extern "C" __declspec(dllexport) VOID WINAPI CchessFlipBoard(PositionStruct *lppos); +extern "C" __declspec(dllexport) LPSTR WINAPI CchessBoardText(const PositionStruct *lppos, BOOL bAnsi); +extern "C" __declspec(dllexport) LPSTR WINAPI CchessBoard2Fen(const PositionStruct *lppos); +extern "C" __declspec(dllexport) VOID WINAPI CchessFen2Board(PositionStruct *lppos, LPCSTR szFen); +extern "C" __declspec(dllexport) LPSTR WINAPI CchessFenMirror(LPCSTR szFenSrc); +extern "C" __declspec(dllexport) LONG WINAPI CchessFileMirror(LONG dwFileStr); +extern "C" __declspec(dllexport) LONG WINAPI CchessChin2File(LONGLONG qwChinStr); +extern "C" __declspec(dllexport) LONGLONG WINAPI CchessFile2Chin(LONG dwFileStr, LONG sd); +extern "C" __declspec(dllexport) LONG WINAPI CchessFile2Move(LONG dwFileStr, const PositionStruct *lppos); +extern "C" __declspec(dllexport) LONG WINAPI CchessMove2File(LONG mv, const PositionStruct *lppos); + +// 驱动程序的版本号,在《象棋巫师》中使用“关于规则”功能可以看到。 +static const char *const cszCchessVersion = "Chinese Chess Driver 3.13"; + +LPCSTR WINAPI CchessVersion(VOID) { + return cszCchessVersion; +} + +VOID WINAPI CchessInit(BOOL bTraditional) { + PreGenInit(); + ChineseInit(bTraditional != FALSE); +} + +VOID WINAPI CchessPromotion(BOOL bPromotion) { + PreEval.bPromotion = bPromotion != FALSE; +} + +VOID WINAPI CchessAddPiece(PositionStruct *lppos, LONG sq, LONG pc, BOOL bDel) { + lppos->AddPiece(sq, pc, bDel != FALSE); +} + +BOOL WINAPI CchessCanPromote(PositionStruct *lppos, LONG sq) { + int pt; + if (PreEval.bPromotion && lppos->CanPromote() && CAN_PROMOTE(sq)) { + pt = PIECE_TYPE(lppos->ucpcSquares[sq]); + return pt == ADVISOR_TYPE || pt == BISHOP_TYPE; + } + return FALSE; +} + +BOOL WINAPI CchessTryMove(PositionStruct *lppos, LPLONG lpStatus, LONG mv) { + return TryMove(*lppos, *(int *) lpStatus, mv); +} + +VOID WINAPI CchessUndoMove(PositionStruct *lppos) { + lppos->UndoMakeMove(); +} + +// 执行“空着”,该功能目前仅用在“搜索树分析器”中 +BOOL WINAPI CchessTryNull(PositionStruct *lppos) { + if (lppos->LastMove().ChkChs > 0) { + return FALSE; + } else { + lppos->NullMove(); + return TRUE; + } +} + +// 撤消“空着”,该功能目前仅用在“搜索树分析器”中 +VOID WINAPI CchessUndoNull(PositionStruct *lppos) { + lppos->UndoNullMove(); +} + +// 生成全部合理着法 +LONG WINAPI CchessGenMoves(PositionStruct *lppos, LPLONG lpmv) { + int i, nTotal, nLegal; + MoveStruct mvs[MAX_GEN_MOVES]; + nTotal = lppos->GenAllMoves(mvs); + nLegal = 0; + for (i = 0; i < nTotal; i ++) { + if (lppos->MakeMove(mvs[i].wmv)) { + lppos->UndoMakeMove(); + lpmv[nLegal] = mvs[i].wmv; + nLegal ++; + } + } + return nLegal; +} + +VOID WINAPI CchessSetIrrev(PositionStruct *lppos) { + lppos->SetIrrev(); +} + +VOID WINAPI CchessClearBoard(PositionStruct *lppos) { + lppos->ClearBoard(); +} + +VOID WINAPI CchessStartBoard(PositionStruct *lppos) { + lppos->FromFen(cszStartFen); +} + +VOID WINAPI CchessBoardMirror(PositionStruct *lppos) { + lppos->Mirror(); +} + +VOID WINAPI CchessExchangeSide(PositionStruct *lppos) { + ExchangeSide(*lppos); +} + +VOID WINAPI CchessFlipBoard(PositionStruct *lppos) { + FlipBoard(*lppos); +} + +LPSTR WINAPI CchessBoardText(const PositionStruct *lppos, BOOL bAnsi) { + static char szBoard[2048]; + BoardText(szBoard, *lppos, bAnsi != FALSE); + return szBoard; +} + +LPSTR WINAPI CchessBoard2Fen(const PositionStruct *lppos) { + static char szFen[128]; + lppos->ToFen(szFen); + return szFen; +} + +VOID WINAPI CchessFen2Board(PositionStruct *lppos, LPCSTR szFen) { + lppos->FromFen(szFen); +} + +LPSTR WINAPI CchessFenMirror(LPCSTR szFenSrc) { + static char szFenDst[128]; + FenMirror(szFenDst, szFenSrc); + return szFenDst; +} + +LONG WINAPI CchessFileMirror(LONG dwFileStr) { + return FileMirror(dwFileStr); +} + +LONG WINAPI CchessChin2File(LONGLONG qwChinStr) { + return Chin2File(qwChinStr); +} + +LONGLONG WINAPI CchessFile2Chin(LONG dwFileStr, LONG sd) { + return File2Chin(dwFileStr, sd); +} + +LONG WINAPI CchessFile2Move(LONG dwFileStr, const PositionStruct *lppos) { + return File2Move(dwFileStr, *lppos); +} + +LONG WINAPI CchessMove2File(LONG mv, const PositionStruct *lppos) { + return Move2File(mv, *lppos); +} + +#endif + +/* ElephantEye源程序使用的匈牙利记号约定: + * + * sq: 格子序号(整数,从0到255,参阅"pregen.cpp") + * pc: 棋子序号(整数,从0到47,参阅"position.cpp") + * pt: 棋子类型序号(整数,从0到6,参阅"position.cpp") + * mv: 着法(整数,从0到65535,参阅"position.cpp") + * sd: 走子方(整数,0代表红方,1代表黑方) + * vl: 局面价值(整数,从"-MATE_VALUE"到"MATE_VALUE",参阅"position.cpp") + * (注:以上五个记号可与uc、dw等代表整数的记号配合使用) + * pos: 局面(PositionStruct类型,参阅"position.h") + * sms: 位行和位列的着法生成预置结构(参阅"pregen.h") + * smv: 位行和位列的着法判断预置结构(参阅"pregen.h") + */ + +/* 以下常量规定了着法表示使用的数字、棋子、方向(进平退)、位置(前后)等的最大个数。 + * + * 表示位置的符号共有8个,除了“前中后”以外还有“一二三四五”,参考 + * 《中国象棋电脑应用规范(二):着法表示》(简称《规范》),即以下网页: + *   http://www.elephantbase.net/protocol/cchess_move.htm + * 由于“前中后”被安排在“一二三四五”以后,但又和“进平退”在符号上一致,因此要加减"DIRECT_TO_POS"作转换。 + * 另外,由于仕(士)相(象)的着法表示的纵线形式和坐标形式有一一对应的关系(固定纵线表示), + * 因此可以使用数组"cdwFixFile"和"cucFixMove"对两者进行转换,总共有28种对应关系。 + */ +const int MAX_DIGIT = 9; +const int MAX_PIECE = 7; +const int MAX_DIRECT = 3; +const int MAX_POS = 8; +const int DIRECT_TO_POS = 5; +const int MAX_FIX_FILE = 28; + +/* 以下是数字、棋子、方向和位置编码对应的符号和汉字。 + * + * 数组长度至少要比这些符号的个数多1,以"ccDirect2Byte"为例,当发现没有方向跟某个符号对应时, + * 该方向编号为"MAX_DIRECT",还原成符号时保证数组不越界,并以空格表示。 + * 汉字数组有简体(GBK码)和繁体(BIG5码)两套,以"cwDirect2Word..."为例,后缀"-Simp"表示简体,"-Trad"表示繁体。 + * 数组在使用前,必须用"lpcwDirect2Word"指针来定位,参阅函数"ChineseInit()"。 + */ + +static const char ccDirect2Byte[4] = { + '+', '.', '-', ' ' +}; + +static const char ccPos2Byte[12] = { + 'a', 'b', 'c', 'd', 'e', '+', '.', '-', ' ', ' ', ' ', ' ' +}; + +static const uint16_t cwDigit2WordSimp[2][10] = { + { + 0xbbd2/*一*/, 0xfeb6/*二*/, 0xfdc8/*三*/, 0xc4cb/*四*/, 0xe5ce/*五*/, + 0xf9c1/*六*/, 0xdfc6/*七*/, 0xcbb0/*八*/, 0xc5be/*九*/, 0xa1a1/* */ + }, { + 0xb1a3/*1*/, 0xb2a3/*2*/, 0xb3a3/*3*/, 0xb4a3/*4*/, 0xb5a3/*5*/, + 0xb6a3/*6*/, 0xb7a3/*7*/, 0xb8a3/*8*/, 0xb9a3/*9*/, 0xa1a1/* */ + } +}; + +static const uint16_t cwPiece2WordSimp[2][8] = { + { + 0xa7cb/*帅*/, 0xcbca/*仕*/, 0xe0cf/*相*/, 0xedc2/*马*/, 0xb5b3/*车*/, 0xdac5/*炮*/, 0xf8b1/*兵*/, 0xa1a1/* */ + }, { + 0xabbd/*将*/, 0xbfca/*士*/, 0xf3cf/*象*/, 0xedc2/*马*/, 0xb5b3/*车*/, 0xdac5/*炮*/, 0xe4d7/*卒*/, 0xa1a1/* */ + } +}; + +static const uint16_t cwDirect2WordSimp[4] = { + 0xf8bd/*进*/, 0xbdc6/*平*/, 0xcbcd/*退*/, 0xa1a1/* */ +}; + +static const uint16_t cwPos2WordSimp[10] = { + 0xbbd2/*一*/, 0xfeb6/*二*/, 0xfdc8/*三*/, 0xc4cb/*四*/, 0xe5ce/*五*/, + 0xb0c7/*前*/, 0xd0d6/*中*/, 0xf3ba/*后*/, 0xa1a1/* */, 0xa1a1/* */ +}; + +static const uint16_t cwDigit2WordTrad[2][10] = { + { + 0x40a4/*[一]*/, 0x47a4/*[二]*/, 0x54a4/*[三]*/, 0x7ca5/*[四]*/, 0xada4/*き[五]*/, + 0xbba4/*せ[六]*/, 0x43a4/*[七]*/, 0x4ba4/*[八]*/, 0x45a4/*[九]*/, 0x40a1/**/ + }, { + 0xb0a2/*[1]*/, 0xb1a2/*⒈[2]*/, 0xb2a2/*⒉[3]*/, 0xb3a2/*⒊[4]*/, 0xb4a2/*⒋[5]*/, + 0xb5a2/*⒌[6]*/, 0xb6a2/*⒍[7]*/, 0xb7a2/*⒎[8]*/, 0xb8a2/*⒏[9]*/, 0x40a1/**/ + } +}; + +static const uint16_t cwPiece2WordTrad[2][8] = { + { + 0xd3ab/*[帥]*/, 0x4ba5/*[仕]*/, 0xdbac/*[相]*/, 0xa8b0/*皑[馬]*/, + 0xaea8/*ó[車]*/, 0xb6ac/*[炮]*/, 0x4ca7/*[兵]*/, 0x40a1/**/ + }, { + 0x4eb1/*盢[將]*/, 0x68a4/*[士]*/, 0x48b6/*禜[象]*/, 0xa8b0/*皑[馬]*/, + 0xaea8/*ó[車]*/, 0xb6ac/*[炮]*/, 0xf2a8/*[卒]*/, 0x40a1/**/ + } +}; + +static const uint16_t cwDirect2WordTrad[4] = { + 0x69b6/*秈[進]*/, 0xada5/*キ[平]*/, 0x68b0/*癶[退]*/, 0x40a1/**/ +}; + +static const uint16_t cwPos2WordTrad[10] = { + 0x40a4/*[一]*/, 0x47a4/*[二]*/, 0x54a4/*[三]*/, 0x7ca5/*[四]*/, 0xada4/*き[五]*/, + 0x65ab/*玡[前]*/, 0xa4a4/*い[中]*/, 0xe1ab/*[後]*/, 0x40a1/**/, 0x40a1/**/ +}; + +// 固定纵线表示的纵线数组 +static const uint32_t cdwFixFile[28] = { + 0x352d3441/*A4-5*/, 0x352b3441/*A4+5*/, 0x342d3541/*A5-4*/, 0x342b3541/*A5+4*/, + 0x362d3541/*A5-6*/, 0x362b3541/*A5+6*/, 0x352d3641/*A6-5*/, 0x352b3641/*A6+5*/, + 0x332d3142/*B1-3*/, 0x332b3142/*B1+3*/, 0x312d3342/*B3-1*/, 0x312b3342/*B3+1*/, + 0x352d3342/*B3-5*/, 0x352b3342/*B3+5*/, 0x332d3542/*B5-3*/, 0x332b3542/*B5+3*/, + 0x372d3542/*B5-7*/, 0x372b3542/*B5+7*/, 0x352d3742/*B7-5*/, 0x352b3742/*B7+5*/, + 0x392d3742/*B7-9*/, 0x392b3742/*B7+9*/, 0x372d3942/*B9-7*/, 0x372b3942/*B9+7*/, + 0x503d3441/*A4=P*/, 0x503d3641/*A6=P*/, 0x503d3342/*B3=P*/, 0x503d3742/*B7=P*/ +}; + +// 固定纵线表示的坐标数组 +static const uint8_t cucFixMove[28][2] = { + {0xa8, 0xb7}, {0xc8, 0xb7}, {0xb7, 0xc8}, {0xb7, 0xa8}, {0xb7, 0xc6}, {0xb7, 0xa6}, {0xa6, 0xb7}, {0xc6, 0xb7}, + {0xab, 0xc9}, {0xab, 0x89}, {0x89, 0xab}, {0xc9, 0xab}, {0x89, 0xa7}, {0xc9, 0xa7}, {0xa7, 0xc9}, {0xa7, 0x89}, + {0xa7, 0xc5}, {0xa7, 0x85}, {0x85, 0xa7}, {0xc5, 0xa7}, {0x85, 0xa3}, {0xc5, 0xa3}, {0xa3, 0xc5}, {0xa3, 0x85}, + {0xc8, 0xc8}, {0xc6, 0xc6}, {0xc9, 0xc9}, {0xc5, 0xc5} +}; + +// 简体文本棋盘的棋盘字符 +static const char *cszBoardStrSimp[19] = { + " ┌--┬--┬--┬--┬--┬--┬--┬--┐ ", + " │ │ │ │\│/│ │ │ │ ", + " ├--┼--┼--┼--※--┼--┼--┼--┤ ", + " │ │ │ │/│\│ │ │ │ ", + " ├--┼--┼--┼--┼--┼--┼--┼--┤ ", + " │ │ │ │ │ │ │ │ │ ", + " ├--┼--┼--┼--┼--┼--┼--┼--┤ ", + " │ │ │ │ │ │ │ │ │ ", + " ├--┴--┴--┴--┴--┴--┴--┴--┤ ", + " │ │ ", + " ├--┬--┬--┬--┬--┬--┬--┬--┤ ", + " │ │ │ │ │ │ │ │ │ ", + " ├--┼--┼--┼--┼--┼--┼--┼--┤ ", + " │ │ │ │ │ │ │ │ │ ", + " ├--┼--┼--┼--┼--┼--┼--┼--┤ ", + " │ │ │ │\│/│ │ │ │ ", + " ├--┼--┼--┼--※--┼--┼--┼--┤ ", + " │ │ │ │/│\│ │ │ │ ", + " └--┴--┴--┴--┴--┴--┴--┴--┘ " +}; + +// 繁体文本棋盘的棋盘字符 +static const char *cszBoardStrTrad[19] = { + " ---------------- ", + "    〓    ", + " --------“-------- ", + "    〓    ", + " ---------------- ", + "          ", + " ---------------- ", + "          ", + " ---------------- ", + "   ", + " ---------------- ", + "          ", + " ---------------- ", + "          ", + " ---------------- ", + "    〓    ", + " --------“-------- ", + "    〓    ", + " ---------------- " +}; + +/* 以下两个数组实现了内部棋盘坐标(Square)和纵线优先坐标(FileSq)的转换。 + * + * 内部棋盘坐标是有3层边界的16x16冗余数组(参阅"pregen.cpp"),为方便转换成纵线格式, + * 要对它们重新编号,即按列优先从右到左,相同的列再从前到后的顺序(参阅《规范》)。 + * 转换后的坐标仍然是16x16的冗余数组,整除16后就是列号(右边线是0),对16取余就是行号(上边线是0)。 + */ + +static const uint8_t cucSquare2FileSq[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0x80, 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10, 0x00, 0, 0, 0, 0, + 0, 0, 0, 0x81, 0x71, 0x61, 0x51, 0x41, 0x31, 0x21, 0x11, 0x01, 0, 0, 0, 0, + 0, 0, 0, 0x82, 0x72, 0x62, 0x52, 0x42, 0x32, 0x22, 0x12, 0x02, 0, 0, 0, 0, + 0, 0, 0, 0x83, 0x73, 0x63, 0x53, 0x43, 0x33, 0x23, 0x13, 0x03, 0, 0, 0, 0, + 0, 0, 0, 0x84, 0x74, 0x64, 0x54, 0x44, 0x34, 0x24, 0x14, 0x04, 0, 0, 0, 0, + 0, 0, 0, 0x85, 0x75, 0x65, 0x55, 0x45, 0x35, 0x25, 0x15, 0x05, 0, 0, 0, 0, + 0, 0, 0, 0x86, 0x76, 0x66, 0x56, 0x46, 0x36, 0x26, 0x16, 0x06, 0, 0, 0, 0, + 0, 0, 0, 0x87, 0x77, 0x67, 0x57, 0x47, 0x37, 0x27, 0x17, 0x07, 0, 0, 0, 0, + 0, 0, 0, 0x88, 0x78, 0x68, 0x58, 0x48, 0x38, 0x28, 0x18, 0x08, 0, 0, 0, 0, + 0, 0, 0, 0x89, 0x79, 0x69, 0x59, 0x49, 0x39, 0x29, 0x19, 0x09, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const uint8_t cucFileSq2Square[256] = { + 0x3b, 0x4b, 0x5b, 0x6b, 0x7b, 0x8b, 0x9b, 0xab, 0xbb, 0xcb, 0, 0, 0, 0, 0, 0, + 0x3a, 0x4a, 0x5a, 0x6a, 0x7a, 0x8a, 0x9a, 0xaa, 0xba, 0xca, 0, 0, 0, 0, 0, 0, + 0x39, 0x49, 0x59, 0x69, 0x79, 0x89, 0x99, 0xa9, 0xb9, 0xc9, 0, 0, 0, 0, 0, 0, + 0x38, 0x48, 0x58, 0x68, 0x78, 0x88, 0x98, 0xa8, 0xb8, 0xc8, 0, 0, 0, 0, 0, 0, + 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0, 0, 0, 0, 0, 0, + 0x36, 0x46, 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0, 0, 0, 0, 0, 0, + 0x35, 0x45, 0x55, 0x65, 0x75, 0x85, 0x95, 0xa5, 0xb5, 0xc5, 0, 0, 0, 0, 0, 0, + 0x34, 0x44, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb4, 0xc4, 0, 0, 0, 0, 0, 0, + 0x33, 0x43, 0x53, 0x63, 0x73, 0x83, 0x93, 0xa3, 0xb3, 0xc3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// 汉字符号的指针,即规定了简体还是繁体,由"ChineseInit()"进行赋值 +static const uint16_t (*lpcwDigit2Word)[10], (*lpcwPiece2Word)[8], *lpcwDirect2Word, *lpcwPos2Word; +static const char **lpcszBoardStr; +static uint16_t wPromote; + +inline uint8_t SQUARE_FILESQ(int sq) { + return cucSquare2FileSq[sq]; +} + +inline uint8_t FILESQ_SQUARE(int sq) { + return cucFileSq2Square[sq]; +} + +inline int FILESQ_RANK_Y(int sq) { + return sq & 15; +} + +inline int FILESQ_FILE_X(int sq) { + return sq >> 4; +} + +inline int FILESQ_COORD_XY(int x, int y) { + return (x << 4) + y; +} + +// 获得某个棋子对于本方视角的纵线优先坐标,棋子编号从0到15 +inline int FILESQ_SIDE_PIECE(const PositionStruct &pos, int nPieceNum) { + int sq; + sq = pos.ucsqPieces[SIDE_TAG(pos.sdPlayer) + nPieceNum]; + return (sq == 0 ? -1 : pos.sdPlayer == 0 ? SQUARE_FILESQ(sq) : SQUARE_FILESQ(SQUARE_FLIP(sq))); +} + +// 根据子力类型获得棋子的编号 +inline int FIRST_PIECE(int pt, int pc) { + return pt * 2 - 1 + pc; +} + +/* 以下函数实现了数字、棋子、方向和位置的编码和符号、编码和汉字之间的转换 + * + * 部分符号编码转换的代码,利用了"position.cpp"中的"PIECE_BYTE"数组和"FenPiece()"函数。 + * 从汉字转换为编码是难点,无论处于简体状态还是繁体状态,转换时既考虑了简体、繁体和异体,也考虑了GBK码和BIG5码, + * 因此除了依次比较汉字数组外,还增加了对GBK码繁体字和异体字的识别。 + */ + +inline int Digit2Byte(int nArg) { + return nArg + '1'; +} + +inline int Byte2Digit(int nArg) { + return (nArg >= '1' && nArg <= '9' ? nArg - '1' : MAX_DIGIT); +} + +inline int Piece2Byte(int nArg) { + return PIECE_BYTE(nArg); +} + +inline int Byte2Piece(int nArg) { + return (nArg >= '1' && nArg <= '7' ? nArg - '1' : nArg >= 'A' && nArg <= 'Z' ? FenPiece(nArg) : + nArg >= 'a' && nArg <= 'z' ? FenPiece(nArg - 'a' + 'A') : MAX_PIECE); +} + +inline int Byte2Direct(int nArg) { + return (nArg == '+' ? 0 : nArg == '.' || nArg == '=' ? 1 : nArg == '-' ? 2 : 3); +} + +inline int Byte2Pos(int nArg) { + return (nArg >= 'a' && nArg <= 'e' ? nArg - 'a' : Byte2Direct(nArg) + DIRECT_TO_POS); +} + +static int Word2Digit(int nArg) { + int i; + for (i = 0; i < MAX_DIGIT; i ++) { + if (nArg == cwDigit2WordSimp[0][i] || nArg == cwDigit2WordSimp[1][i] || + nArg == cwDigit2WordTrad[0][i] || nArg == cwDigit2WordTrad[1][i]) { + break; + } + } + return i; +} + +static int Word2Piece(int nArg) { + int i; + if (false) { + } else if (nArg == 0x9b8e/*帥*/ || nArg == 0xa28c/*將*/) { + return 0; + } else if (nArg == 0x52f1/*馬*/ || nArg == 0xd882/*傌*/ || nArg == 0x58d8/*豖[傌]*/) { + return 3; + } else if (nArg == 0x87dc/*車*/ || nArg == 0x8cb3/*硨*/ || nArg == 0xcfda/*谙[硨]*/ || nArg == 0x6582 /*俥*/) { + return 4; + } else if (nArg == 0xfcb0/*包*/ || nArg == 0x5da5/*[包]*/ || nArg == 0x68b3/*砲*/ || nArg == 0xa5af/*[砲]*/) { + return 5; + } else { + for (i = 0; i < MAX_PIECE; i ++) { + if (nArg == cwPiece2WordSimp[0][i] || nArg == cwPiece2WordSimp[1][i] || + nArg == cwPiece2WordTrad[0][i] || nArg == cwPiece2WordTrad[1][i]) { + break; + } + } + return i; + } +} + +static int Word2Direct(int nArg) { + int i; + if (nArg == 0x4ddf/*進*/) { + return 0; + } else { + for (i = 0; i < MAX_DIRECT; i ++) { + if (nArg == cwDirect2WordSimp[i] || nArg == cwDirect2WordTrad[i]) { + break; + } + } + return i; + } +} + +static int Word2Pos(int nArg) { + int i; + if (nArg == 0xe1e1/*後*/ || nArg == 0x5aa6/*[后]*/) { + return 2 + DIRECT_TO_POS; + } else { + for (i = 0; i < MAX_POS; i ++) { + if (nArg == cwPos2WordSimp[i] || nArg == cwPos2WordTrad[i]) { + break; + } + } + return i; + } +} + +// 确定使用简体汉字和繁体汉字 +void ChineseInit(bool bTraditional) { + if (bTraditional) { + lpcwDigit2Word = cwDigit2WordTrad; + lpcwPiece2Word = cwPiece2WordTrad; + lpcwDirect2Word = cwDirect2WordTrad; + lpcwPos2Word = cwPos2WordTrad; + lpcszBoardStr = cszBoardStrTrad; + wPromote = 0xdcc5/*跑*/; + } else { + lpcwDigit2Word = cwDigit2WordSimp; + lpcwPiece2Word = cwPiece2WordSimp; + lpcwDirect2Word = cwDirect2WordSimp; + lpcwPos2Word = cwPos2WordSimp; + lpcszBoardStr = cszBoardStrSimp; + wPromote = 0xe4b1/*变*/; + } +} + +// 尝试某个着法,并返回着法状态,参阅"cchess.h" +bool TryMove(PositionStruct &pos, int &nStatus, int mv) { + if (!pos.LegalMove(mv)) { + nStatus = MOVE_ILLEGAL; + return false; + } + if (!pos.MakeMove(mv)) { + nStatus = MOVE_INCHECK; + return false; + } + nStatus = 0; + nStatus += (pos.LastMove().CptDrw > 0 ? MOVE_CAPTURE : 0); + nStatus += (pos.LastMove().ChkChs > 0 ? MOVE_CHECK : 0); + nStatus += (pos.IsMate() ? MOVE_MATE : 0); + nStatus += pos.RepStatus(3) * MOVE_PERPETUAL; // 提示:参阅"position.cpp"中的"IsRep()"函数 + nStatus += (pos.IsDraw() ? MOVE_DRAW : 0); + return true; +} + +// 局面镜像 + +// 红黑互换 +void ExchangeSide(PositionStruct &pos) { + int i, sq; + uint8_t ucsqList[32]; + for (i = 16; i < 48; i ++) { + sq = pos.ucsqPieces[i]; + ucsqList[i - 16] = sq; + if (sq != 0) { + pos.AddPiece(sq, i, DEL_PIECE); + } + } + for (i = 16; i < 48; i ++) { + sq = ucsqList[i < 32 ? i : i - 32]; // 这行不同于FlipBoard + if (sq != 0) { + pos.AddPiece(SQUARE_FLIP(sq), i); + } + } + pos.ChangeSide(); // 这行不同于FlipBoard +} + +// 翻转棋盘 +void FlipBoard(PositionStruct &pos) { + int i, sq; + uint8_t ucsqList[32]; + for (i = 16; i < 48; i ++) { + sq = pos.ucsqPieces[i]; + ucsqList[i - 16] = sq; + if (sq != 0) { + pos.AddPiece(sq, i, DEL_PIECE); + } + } + for (i = 16; i < 48; i ++) { + sq = ucsqList[i - 16]; // 这行不同于ExchangeSide + if (sq != 0) { + pos.AddPiece(SQUARE_FLIP(sq), i); + } + } +} + +// 生成文本棋盘(红子用()表示,黑子用[]表示) +void BoardText(char *szBoard, const PositionStruct &pos, bool bAnsi) { + char *lpBoard; + int i, j, pc; + + lpBoard = szBoard; + if (bAnsi) { + lpBoard += sprintf(lpBoard, "\33[0m"); + } + for (i = 0; i < 19; i ++) { + if (i % 2 == 0) { + for (j = FILE_LEFT; j <= FILE_RIGHT; j ++) { + pc = pos.ucpcSquares[COORD_XY(j, i / 2 + RANK_TOP)]; + if ((pc & SIDE_TAG(0)) != 0) { + lpBoard += sprintf(lpBoard, bAnsi ? "(\33[1;31m%.2s\33[0m)" : + "(%.2s)", (const char *) &lpcwPiece2Word[0][PIECE_TYPE(pc)]); + } else if ((pc & SIDE_TAG(1)) != 0) { + lpBoard += sprintf(lpBoard, bAnsi ? "[\33[1;32m%.2s\33[0m]" : + "[%.2s]", (const char *) &lpcwPiece2Word[1][PIECE_TYPE(pc)]); + } else { + lpBoard += sprintf(lpBoard, "%.4s", lpcszBoardStr[i] + (j - FILE_LEFT) * 4); + } + } + lpBoard += sprintf(lpBoard, "\r\n"); + } else { + lpBoard += sprintf(lpBoard, "%s\r\n", lpcszBoardStr[i]); + } + } +} + +// 对FEN串作镜像(只要识别行分隔符"/",行内字符串顺序颠倒即可) +void FenMirror(char *szFenDst, const char *szFenSrc) { + int i, j; + const char *lpSrc; + char *lpDst, *lpDstLimit; + char szTempStr[128]; + + lpSrc = szFenSrc; + lpDst = szFenDst; + lpDstLimit = lpDst + 127; + if (*lpSrc == '\0') { + *lpDst = '\0'; + return; + } + while (*lpSrc == ' ') { + lpSrc ++; + if (*lpSrc == '\0') { + *lpDst = '\0'; + return; + } + } + i = 0; + while(lpDst < lpDstLimit && i < 127) { + if (*lpSrc == '/' || *lpSrc == ' ' || *lpSrc == '\0') { + for (j = 0; j < i; j ++) { + *lpDst = szTempStr[i - j - 1]; + lpDst ++; + if (lpDst == lpDstLimit) { + break; + } + } + i = 0; + if (*lpSrc == '/') { + *lpDst = '/'; + lpDst ++; + } else { + break; + } + } else { + szTempStr[i] = *lpSrc; + i ++; + } + lpSrc ++; + }; + while(lpSrc != '\0' && lpDst < lpDstLimit) { + *lpDst = *lpSrc; + lpSrc ++; + lpDst ++; + } + *lpDst = '\0'; + return; +} + +union C4dwStruct { + char c[4]; + uint32_t dw; +}; + +/* 函数"FileMirror()"对着法的纵线表示作镜像。 + * + * 纵线的符号表示基本类似于汉字表示,但当出现类似“前炮退二”这样的表示时,符号表示就会有不同的情况。 + * 按照《规范》的建议,表示成"C+-2"最容易被识别,但是也有表示成"+C-2"的,即符号和汉字完全对应,因此本函数也会考虑这种形式。 + * 对一般着法而言,纵线表示的镜像是唯一的,但是对于“两条的纵线上有多个兵(卒)”的罕见情况, + * 本函数只能考虑最不罕见的一种特例,即两条纵线上各有两个兵(卒),这样,"Paxx"和"Pbxx"分别跟"Pcxx"和"Pdxx"镜像, + * 而对于其他情况则无法作出正确转换。 + * 注意:符号表示由4个字节构成,所以可以用一个"uint32_t"类型作快速传输(同理,汉字表示用"uint64_t")。 + */ +uint32_t FileMirror(uint32_t dwFileStr) { + int nPos, nFile, pt; + C4dwStruct Ret; + Ret.dw = dwFileStr; + + nPos = Byte2Direct(Ret.c[0]); + if (nPos == MAX_DIRECT) { + pt = Byte2Piece(Ret.c[0]); + nFile = Byte2Digit(Ret.c[1]); + if (nFile == MAX_DIGIT) { + switch (Ret.c[1]) { + case 'a': + Ret.c[1] = 'c'; + break; + case 'b': + Ret.c[1] = 'd'; + break; + case 'c': + Ret.c[1] = 'a'; + break; + case 'd': + Ret.c[1] = 'b'; + break; + default: + break; + } + } else { + Ret.c[1] = Digit2Byte(8 - nFile); + } + } else { + pt = Byte2Piece(Ret.c[1]); + } + if ((pt >= ADVISOR_TYPE && pt <= KNIGHT_TYPE) || Byte2Direct(Ret.c[2]) == 1) { + Ret.c[3] = Digit2Byte(8 - Byte2Digit(Ret.c[3])); + } + return Ret.dw; +} + +// 将汉字表示转换为符号表示 +uint32_t Chin2File(uint64_t qwChinStr) { + int nPos; + uint16_t *lpwArg; + C4dwStruct Ret; + + lpwArg = (uint16_t *) (void *) &qwChinStr; + nPos = Word2Pos(lpwArg[0]); + Ret.c[0] = PIECE_BYTE(Word2Piece(nPos == MAX_POS ? lpwArg[0] : lpwArg[1])); + Ret.c[1] = (nPos == MAX_POS ? Digit2Byte(Word2Digit(lpwArg[1])) : ccPos2Byte[nPos]); + if ((lpwArg[2] == 0xe4b1/*变*/ || lpwArg[2] == 0xdcc5/*跑*/ || lpwArg[2] == 0x83d7/*變*/) && + Word2Piece(lpwArg[3]) == 6) { + Ret.c[2] = '='; + Ret.c[3] = 'P'; + } else { + Ret.c[2] = ccDirect2Byte[Word2Direct(lpwArg[2])]; + Ret.c[3] = Digit2Byte(Word2Digit(lpwArg[3])); + } + return Ret.dw; +} + +// 将符号表示转换为汉字表示 +uint64_t File2Chin(uint32_t dwFileStr, int sdPlayer) { + int nPos; + char *lpArg; + union { + uint16_t w[4]; + uint64_t qw; + } Ret; + + lpArg = (char *) &dwFileStr; + nPos = Byte2Direct(lpArg[0]); + if (nPos == MAX_DIRECT) { + nPos = Byte2Pos(lpArg[1]); + Ret.w[0] = (nPos == MAX_POS ? lpcwPiece2Word[sdPlayer][Byte2Piece(lpArg[0])] : lpcwPos2Word[nPos]); + Ret.w[1] = (nPos == MAX_POS ? lpcwDigit2Word[sdPlayer][Byte2Digit(lpArg[1])] : + lpcwPiece2Word[sdPlayer][Byte2Piece(lpArg[0])]); + } else { + Ret.w[0] = lpcwPos2Word[nPos + DIRECT_TO_POS]; + Ret.w[1] = lpcwPiece2Word[sdPlayer][Byte2Piece(lpArg[1])]; + } + if (lpArg[2] == '=' && Byte2Piece(lpArg[3]) == 6) { + Ret.w[2] = wPromote; + Ret.w[3] = lpcwPiece2Word[sdPlayer][6]; + } else { + Ret.w[2] = lpcwDirect2Word[Byte2Direct(lpArg[2])]; + Ret.w[3] = lpcwDigit2Word[sdPlayer][Byte2Digit(lpArg[3])]; + } + return Ret.qw; +} + +/* "File2Move()"函数将纵线符号表示转换为内部着法表示。 + * + * 这个函数以及后面的"Move2File()"函数是本模块最难处理的两个函数,特别是在处理“两条的纵线上有多个兵(卒)”的问题上。 + * 在棋谱的快速时,允许只使用数字键盘,因此1到7依次代表帅(将)到兵(卒)这七种棋子,"File2Move()"函数也考虑到了这个问题。 + */ +int File2Move(uint32_t dwFileStr, const PositionStruct &pos) { + int i, j, nPos, pt, sq, nPieceNum; + int xSrc, ySrc, xDst, yDst; + C4dwStruct FileStr; + int nFileList[9], nPieceList[5]; + // 纵线符号表示转换为内部着法表示,通常分为以下几个步骤: + + // 1. 检查纵线符号是否是仕(士)相(象)的28种固定纵线表示,在这之前首先必须把数字、小写等不统一的格式转换为统一格式; + FileStr.dw = dwFileStr; + switch (FileStr.c[0]) { + case '2': + case 'a': + FileStr.c[0] = 'A'; + break; + case '3': + case 'b': + case 'E': + case 'e': + FileStr.c[0] = 'B'; + break; + default: + break; + } + if (FileStr.c[3] == 'p') { + FileStr.c[3] = 'P'; + } + for (i = 0; i < MAX_FIX_FILE; i ++) { + if (FileStr.dw == cdwFixFile[i]) { + if (pos.sdPlayer == 0) { + return MOVE(cucFixMove[i][0], cucFixMove[i][1]); + } else { + return MOVE(SQUARE_FLIP(cucFixMove[i][0]), SQUARE_FLIP(cucFixMove[i][1])); + } + } + } + + // 2. 如果不是这28种固定纵线表示,那么把棋子、位置和纵线序号(列号)解析出来 + nPos = Byte2Direct(FileStr.c[0]); + if (nPos == MAX_DIRECT) { + pt = Byte2Piece(FileStr.c[0]); + nPos = Byte2Pos(FileStr.c[1]); + } else { + pt = Byte2Piece(FileStr.c[1]); + nPos += DIRECT_TO_POS; + } + if (nPos == MAX_POS) { + + // 3. 如果棋子是用列号表示的,那么可以直接根据纵线来找到棋子序号; + xSrc = Byte2Digit(FileStr.c[1]); + if (pt == KING_TYPE) { + sq = FILESQ_SIDE_PIECE(pos, 0); + } else if (pt >= KNIGHT_TYPE && pt <= PAWN_TYPE) { + j = (pt == PAWN_TYPE ? 5 : 2); + for (i = 0; i < j; i ++) { + sq = FILESQ_SIDE_PIECE(pos, FIRST_PIECE(pt, i)); + if (sq != -1) { + if (FILESQ_FILE_X(sq) == xSrc) { + break; + } + } + } + sq = (i == j ? -1 : sq); + } else { + sq = -1; + } + } else { + + // 4. 如果棋子是用位置表示的,那么必须挑选出含有多个该种棋子的所有纵线,这是本函数最难处理的地方; + if (pt >= KNIGHT_TYPE && pt <= PAWN_TYPE) { + for (i = 0; i < 9; i ++) { + nFileList[i] = 0; + } + j = (pt == PAWN_TYPE ? 5 : 2); + for (i = 0; i < j; i ++) { + sq = FILESQ_SIDE_PIECE(pos, FIRST_PIECE(pt, i)); + if (sq != -1) { + nFileList[FILESQ_FILE_X(sq)] ++; + } + } + nPieceNum = 0; + for (i = 0; i < j; i ++) { + sq = FILESQ_SIDE_PIECE(pos, FIRST_PIECE(pt, i)); + if (sq != -1) { + if (nFileList[FILESQ_FILE_X(sq)] > 1) { + nPieceList[nPieceNum] = FIRST_PIECE(pt, i); + nPieceNum ++; + } + } + } + + // 5. 找到这些纵线以后,对这些纵线上的棋子进行排序,然后根据位置来确定棋子序号; + for (i = 0; i < nPieceNum - 1; i ++) { + for (j = nPieceNum - 1; j > i; j --) { + if (FILESQ_SIDE_PIECE(pos, nPieceList[j - 1]) > FILESQ_SIDE_PIECE(pos, nPieceList[j])) { + SWAP(nPieceList[j - 1], nPieceList[j]); + } + } + } + // 提示:如果只有两个棋子,那么“后”表示第二个棋子,如果有多个棋子, + // 那么“一二三四五”依次代表第一个到第五个棋子,“前中后”依次代表第一个到第三个棋子。 + if (nPieceNum == 2 && nPos == 2 + DIRECT_TO_POS) { + sq = FILESQ_SIDE_PIECE(pos, nPieceList[1]); + } else { + nPos -= (nPos >= DIRECT_TO_POS ? DIRECT_TO_POS : 0); + sq = (nPos >= nPieceNum ? -1 : FILESQ_SIDE_PIECE(pos, nPieceList[nPos])); + } + } else { + sq = -1; + } + } + if (sq == -1) { + return 0; + } + + // 6. 现在已知了着法的起点,就可以根据纵线表示的后两个符号来确定着法的终点; + xSrc = FILESQ_FILE_X(sq); + ySrc = FILESQ_RANK_Y(sq); + if (pt == KNIGHT_TYPE) { + // 提示:马的进退处理比较特殊。 + xDst = Byte2Digit(FileStr.c[3]); + if (FileStr.c[2] == '+') { + yDst = ySrc - 3 + ABS(xDst - xSrc); + } else { + yDst = ySrc + 3 - ABS(xDst - xSrc); + } + } else { + if (FileStr.c[2] == '+') { + xDst = xSrc; + yDst = ySrc - Byte2Digit(FileStr.c[3]) - 1; + } else if (FileStr.c[2] == '-') { + xDst = xSrc; + yDst = ySrc + Byte2Digit(FileStr.c[3]) + 1; + } else { + xDst = Byte2Digit(FileStr.c[3]); + yDst = ySrc; + } + } + // 注意:yDst有可能超过范围! + if (yDst < 0 || yDst > 9) { + return 0; + } + + // 7. 把相对走子方的坐标转换为固定坐标,得到着法的起点和终点。 + if (pos.sdPlayer == 0) { + return MOVE(FILESQ_SQUARE(FILESQ_COORD_XY(xSrc, ySrc)), FILESQ_SQUARE(FILESQ_COORD_XY(xDst, yDst))); + } else { + return MOVE(SQUARE_FLIP(FILESQ_SQUARE(FILESQ_COORD_XY(xSrc, ySrc))), + SQUARE_FLIP(FILESQ_SQUARE(FILESQ_COORD_XY(xDst, yDst)))); + } +} + +// 将内部着法表示转换为纵线符号 +uint32_t Move2File(int mv, const PositionStruct &pos) { + int i, j, sq, pc, pt, nPieceNum; + int xSrc, ySrc, xDst, yDst; + int nFileList[9], nPieceList[5]; + C4dwStruct Ret; + + if (SRC(mv) == 0 || DST(mv) == 0) { + return 0x20202020; + } + pc = pos.ucpcSquares[SRC(mv)]; + if (pc == 0) { + return 0x20202020; + } + pt = PIECE_TYPE(pc); + Ret.c[0] = PIECE_BYTE(pt); + if (pos.sdPlayer == 0) { + xSrc = FILESQ_FILE_X(SQUARE_FILESQ(SRC(mv))); + ySrc = FILESQ_RANK_Y(SQUARE_FILESQ(SRC(mv))); + xDst = FILESQ_FILE_X(SQUARE_FILESQ(DST(mv))); + yDst = FILESQ_RANK_Y(SQUARE_FILESQ(DST(mv))); + } else { + xSrc = FILESQ_FILE_X(SQUARE_FILESQ(SQUARE_FLIP(SRC(mv)))); + ySrc = FILESQ_RANK_Y(SQUARE_FILESQ(SQUARE_FLIP(SRC(mv)))); + xDst = FILESQ_FILE_X(SQUARE_FILESQ(SQUARE_FLIP(DST(mv)))); + yDst = FILESQ_RANK_Y(SQUARE_FILESQ(SQUARE_FLIP(DST(mv)))); + } + if (pt >= KING_TYPE && pt <= BISHOP_TYPE) { + Ret.c[1] = Digit2Byte(xSrc); + } else { + for (i = 0; i < 9; i ++) { + nFileList[i] = 0; + } + j = (pt == PAWN_TYPE ? 5 : 2); + for (i = 0; i < j; i ++) { + sq = FILESQ_SIDE_PIECE(pos, FIRST_PIECE(pt, i)); + if (sq != -1) { + nFileList[FILESQ_FILE_X(sq)] ++; + } + } + // 提示:处理“两条的纵线上有多个兵(卒)”的问题上,可参阅"File2Move()"函数。 + if (nFileList[xSrc] > 1) { + nPieceNum = 0; + for (i = 0; i < j; i ++) { + sq = FILESQ_SIDE_PIECE(pos, FIRST_PIECE(pt, i)); + if (sq != -1) { + if (nFileList[FILESQ_FILE_X(sq)] > 1) { + nPieceList[nPieceNum] = FIRST_PIECE(pt, i); + nPieceNum ++; + } + } + } + for (i = 0; i < nPieceNum - 1; i ++) { + for (j = nPieceNum - 1; j > i; j --) { + if (FILESQ_SIDE_PIECE(pos, nPieceList[j - 1]) > FILESQ_SIDE_PIECE(pos, nPieceList[j])) { + SWAP(nPieceList[j - 1], nPieceList[j]); + } + } + } + sq = FILESQ_COORD_XY(xSrc, ySrc); + for (i = 0; i < nPieceNum; i ++) { + if (FILESQ_SIDE_PIECE(pos, nPieceList[i]) == sq) { + break; + } + } + Ret.c[1] = (nPieceNum == 2 && i == 1 ? ccPos2Byte[2 + DIRECT_TO_POS] : + ccPos2Byte[nPieceNum > 3 ? i : i + DIRECT_TO_POS]); + } else { + Ret.c[1] = Digit2Byte(xSrc); + } + } + if (pt >= ADVISOR_TYPE && pt <= KNIGHT_TYPE) { + if (SRC(mv) == DST(mv)) { + Ret.c[2] = '='; + Ret.c[3] = 'P'; + } else { + Ret.c[2] = (yDst > ySrc ? '-' : '+'); + Ret.c[3] = Digit2Byte(xDst); + } + } else { + Ret.c[2] = (yDst == ySrc ? '.' : yDst > ySrc ? '-' : '+'); + Ret.c[3] = (yDst == ySrc ? Digit2Byte(xDst) : Digit2Byte(ABS(ySrc - yDst) - 1)); + } + return Ret.dw; +} diff --git a/cchess/cchess.h b/cchess/cchess.h new file mode 100644 index 0000000..7cabc6d --- /dev/null +++ b/cchess/cchess.h @@ -0,0 +1,52 @@ +/* +cchess.h/cchess.cpp - Source Code for ElephantEye, Additional Part + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 2.2, Last Modified: Apr. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../base/base.h" +#include "../eleeye/position.h" + +#ifndef CCHESS_H +#define CCHESS_H + +void ChineseInit(bool bTraditional = false); +bool TryMove(PositionStruct &pos, int &nStatus, int mv); +void ExchangeSide(PositionStruct &pos); +void FlipBoard(PositionStruct &pos); +void BoardText(char *szBoard, const PositionStruct &pos, bool bAnsi = false); +void FenMirror(char *szFenDst, const char *szFenSrc); +uint32_t FileMirror(uint32_t dwFileStr); +uint32_t Chin2File(uint64_t qwChinStr); +uint64_t File2Chin(uint32_t dwFileStr, int sdPlayer); +int File2Move(uint32_t dwFileStr, const PositionStruct &pos); +uint32_t Move2File(int mv, const PositionStruct &pos); + +// 以下常量规定了"TryMove()"的返回状态 +const int MOVE_ILLEGAL = 256; // 不合法的着法 +const int MOVE_INCHECK = 128; // 因将军而不合法的着法 +const int MOVE_DRAW = 64; // 和棋着法(仍被理解为合法的,下同) +const int MOVE_PERPETUAL_LOSS = 32; // 长打的重复着法 +const int MOVE_PERPETUAL_WIN = 16; // 对方长打的重复着法 +const int MOVE_PERPETUAL = 8; // 重复三次的着法 +const int MOVE_MATE = 4; // 将死(包括困毙) +const int MOVE_CHECK = 2; // 将军 +const int MOVE_CAPTURE = 1; // 吃子 + +#endif diff --git a/cchess/ecco.h b/cchess/ecco.h new file mode 100644 index 0000000..e27f5ca --- /dev/null +++ b/cchess/ecco.h @@ -0,0 +1,78 @@ +#ifndef ECCO_H +#define ECCO_H + +#ifdef _WIN32 + +#include +#include "../base/base.h" + +const char *const cszLibEccoFile = "ECCO.DLL"; + +struct EccoApiStruct { + HMODULE hModule; + VOID (WINAPI *EccoInitOpenVar)(LONG); + LONG (WINAPI *EccoIndex)(LPCSTR); + LPCSTR (WINAPI *EccoOpening)(LONG); + LPCSTR (WINAPI *EccoVariation)(LONG); + bool Startup(const char *szLibEccoPath, bool bTrad = false) { + hModule = LoadLibrary(szLibEccoPath); + if (hModule != NULL) { + EccoInitOpenVar = (VOID (WINAPI *)(LONG)) GetProcAddress(hModule, "_EccoInitOpenVar@4"); + EccoIndex = (LONG (WINAPI *)(LPCSTR)) GetProcAddress(hModule, "_EccoIndex@4"); + EccoOpening = (LPCSTR (WINAPI *)(LONG)) GetProcAddress(hModule, "_EccoOpening@4"); + EccoVariation = (LPCSTR (WINAPI *)(LONG)) GetProcAddress(hModule, "_EccoVariation@4"); + EccoInitOpenVar(false); + return true; + } else { + return false; + } + } + bool Available(void) const { + return hModule != NULL; + } + void Shutdown(void) { + if (hModule != NULL) { + FreeLibrary(hModule); + } + } +}; + +#else + +#include +#include "../base/base.h" + +const char *const cszLibEccoFile = "libecco.so"; + +struct EccoApiStruct { + void *hModule; + void (*EccoInitOpenVar)(int); + uint32_t (*EccoIndex)(const char *); + const char *(*EccoOpening)(uint32_t); + const char *(*EccoVariation)(uint32_t); + bool Startup(const char *szLibEccoPath, bool bTrad = false) { + hModule = dlopen(szLibEccoPath, RTLD_LAZY); + if (hModule != NULL) { + EccoInitOpenVar = (void (*)(int)) dlsym(hModule, "EccoInitOpenVar"); + EccoIndex = (uint32_t (*)(const char *)) dlsym(hModule, "EccoIndex"); + EccoOpening = (const char *(*)(uint32_t)) dlsym(hModule, "EccoOpening"); + EccoVariation = (const char *(*)(uint32_t)) dlsym(hModule, "EccoVariation"); + EccoInitOpenVar(false); + return true; + } else { + return false; + } + } + bool Available(void) const { + return hModule != NULL; + } + void Shutdown(void) { + if (hModule != NULL) { + dlclose(hModule); + } + } +}; + +#endif + +#endif diff --git a/cchess/pgnfile.cpp b/cchess/pgnfile.cpp new file mode 100644 index 0000000..3f2fe96 --- /dev/null +++ b/cchess/pgnfile.cpp @@ -0,0 +1,391 @@ +/* +PGN->XQF - a Chinese Chess Score Convertion Program +Designed by Morning Yellow, Version: 2.1, Last Modified: Jun. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include "../base/base.h" +#include "../base/parse.h" +#include "../eleeye/position.h" +#include "../cchess/cchess.h" +#include "pgnfile.h" + +static const char *const cszResult[4] = { + "*", "1-0", "1/2-1/2", "0-1" +}; + +void PgnFileStruct::Init(void) { + int i; + szEvent[0] = szRound[0] = szDate[0] = szSite[0] = '\0'; + szRedTeam[0] = szRed[0] = szRedElo[0] = '\0'; + szBlackTeam[0] = szBlack[0] = szBlackElo[0] = '\0'; + szEcco[0] = szOpen[0] = szVar[0] = '\0'; + nMaxMove = nResult = 0; + posStart.ClearBoard(); + posStart.SetIrrev(); + for (i = 0; i < MAX_MOVE_LEN; i ++) { + szCommentTable[i] = NULL; + } +} + +PgnFileStruct::~PgnFileStruct(void) { + int i; + for (i = 0; i < MAX_MOVE_LEN; i ++) { + if (szCommentTable[i] != NULL) { + delete[] szCommentTable[i]; + } + } +} + +static bool GetLabel(char *szDestStr, const char *szLineStr, const char *szLabelName) { + int nValueLen; + const char *lpLabelEnd; + char szTempLabel[MAX_STR_LEN]; + strcpy(szTempLabel, "["); + strcat(szTempLabel, szLabelName); + strcat(szTempLabel, " \""); + if (StrEqvSkip(szLineStr, szTempLabel)) { + lpLabelEnd = strchr(szLineStr, '\"'); + if (lpLabelEnd == NULL) { + nValueLen = strlen(szLineStr) - 1; + } else { + nValueLen = lpLabelEnd - szLineStr; + } + if (nValueLen >= MAX_STR_LEN) { + nValueLen = MAX_STR_LEN - 1; + } + strncpy(szDestStr, szLineStr, nValueLen); + szDestStr[nValueLen] = '\0'; + return true; + } else { + return false; + } +} + +static void AppendStr(char *&szDst, const char *szSrc) { + int nDstLen; + if (szDst == NULL) { + szDst = new char[MAX_REM_LEN]; + szDst[0] = '\0'; + } + nDstLen = strlen(szDst); + strncpy(szDst + nDstLen, szSrc, MAX_REM_LEN - nDstLen - 1); + szDst[MAX_REM_LEN - 1] = '\0'; +} + +inline int ICCS_MOVE(const char *szIccsStr) { + int sqSrc, sqDst; + sqSrc = COORD_XY(szIccsStr[0] - 'A' + FILE_LEFT, '9' + RANK_TOP - szIccsStr[1]); + sqDst = COORD_XY(szIccsStr[3] - 'A' + FILE_LEFT, '9' + RANK_TOP - szIccsStr[4]); + return MOVE(sqSrc, sqDst); +} + +static const char *const cszAdvertStr = "\r\n" + "============================\r\n" + " 欢迎访问《象棋百科全书网》 \r\n" + " 推荐用《象棋巫师》观赏棋谱 \r\n" + "http://www.elephantbase.net/\r\n"; + +bool PgnFileStruct::Read(const char *szFileName, bool bNoAdvert) { + FILE *fp; + int nRemLevel, nRemLen; + bool bReturned, bDetail, bEndFor; + int nNotation, nCounter, nStatus, mv; + const char *lpLineChar; + char szLineStr[MAX_STR_LEN], szRem[MAX_REM_LEN]; + PositionStruct pos; + + Reset(); + fp = fopen(szFileName, "rb"); + if (fp == NULL) { + return false; + } + posStart.FromFen("rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w - - 0 1"); + bReturned = true; + bDetail = false; + nRemLevel = 0; + nRemLen = 0; + nNotation = 0; + nCounter = 1; + pos = posStart; + lpLineChar = NULL; + while (!(feof(fp) && bReturned)) { + if (bReturned) { + fgets(szLineStr, 256, fp); + lpLineChar = szLineStr; + bReturned = false; + } + if (bDetail) { + if (nRemLevel > 0) { + bEndFor = true; + while (*lpLineChar != '\0' && *lpLineChar != '\r' && *lpLineChar != '\n') { + nRemLevel += (*lpLineChar == '(' || *lpLineChar == '{' ? 1 : *lpLineChar == ')' || *lpLineChar == '}' ? -1 : 0); + if (nRemLevel == 0) { + szRem[nRemLen] = '\0'; + AppendStr(szCommentTable[nMaxMove], szRem); + nRemLen = 0; + bEndFor = false; + lpLineChar ++; + break; + } else { + if (nRemLen < MAX_STR_LEN) { + szRem[nRemLen] = *lpLineChar; + nRemLen ++; + } + } + lpLineChar ++; + } + if (bEndFor) { + szRem[nRemLen] = '\0'; + AppendStr(szCommentTable[nMaxMove], szRem); + nRemLen = 0; + if (*lpLineChar == '\r' || *lpLineChar == '\n') { + AppendStr(szCommentTable[nMaxMove], "\r\n"); + } + bReturned = true; + } + } else { + bEndFor = true; + while (*lpLineChar != '\0' && *lpLineChar != '\r' && *lpLineChar != '\n') { + switch (*lpLineChar) { + case '(': + case '{': + nRemLevel ++; + bEndFor = false; + break; + case '0': + if (strncmp(lpLineChar, "0-1", 3) == 0) { + if (!bNoAdvert) { + AppendStr(szCommentTable[nMaxMove], cszAdvertStr); + } + fclose(fp); + return true; + } + break; + case '1': + if (strncmp(lpLineChar, "1-0", 3) == 0 || strncmp(lpLineChar, "1/2-1/2", 7) == 0) { + if (!bNoAdvert) { + AppendStr(szCommentTable[nMaxMove], cszAdvertStr); + } + fclose(fp); + return true; + } + break; + case '*': + if (!bNoAdvert) { + AppendStr(szCommentTable[nMaxMove], cszAdvertStr); + } + fclose(fp); + return true; + break; + default: + if (nNotation > 0) { + if ((*lpLineChar >= 'A' && *lpLineChar <= 'Z') || (*lpLineChar >= 'a' && *lpLineChar <= 'z') || *lpLineChar == '+' || *lpLineChar == '-' || *lpLineChar == '=') { + if (nNotation == 1) { + mv = File2Move(*(long *) lpLineChar, pos); + } else { + mv = ICCS_MOVE(lpLineChar); + } + if (TryMove(pos, nStatus, mv)) { + if (pos.nMoveNum == MAX_MOVE_NUM) { + pos.SetIrrev(); + } + if (nCounter < MAX_COUNTER) { + if (pos.sdPlayer == 0) { + nCounter ++; + } + nMaxMove ++; + wmvMoveTable[nMaxMove] = mv; + if (nNotation == 1) { + lpLineChar += 3; + } else { + lpLineChar += 5; + } + } + } + bEndFor = false; + break; + } + } else { + if (*lpLineChar < 0) { + mv = File2Move(Chin2File(*(uint64_t *) lpLineChar), pos); + if (TryMove(pos, nStatus, mv)) { + if (pos.nMoveNum == MAX_MOVE_NUM) { + pos.SetIrrev(); + } + if (nCounter < MAX_COUNTER) { + if (pos.sdPlayer == 0) { + nCounter ++; + } + nMaxMove ++; + wmvMoveTable[nMaxMove] = mv; + lpLineChar += 7; + } + } + } + } + break; + } + lpLineChar ++; + if (!bEndFor) { + break; + } + } + if (bEndFor) { + bReturned = true; + } + } + } else { + if (szLineStr[0] == '\0') { + bReturned = true; + } else if (szLineStr[0] == '[') { + if (false) { + } else if (GetLabel(szEvent, szLineStr, "EVENT")) { + } else if (GetLabel(szRound, szLineStr, "ROUND")) { + } else if (GetLabel(szDate, szLineStr, "DATE")) { + } else if (GetLabel(szSite, szLineStr, "SITE")) { + } else if (GetLabel(szRedTeam, szLineStr, "REDTEAM")) { + } else if (GetLabel(szRed, szLineStr, "RED")) { + } else if (GetLabel(szRedElo, szLineStr, "REDELO")) { + } else if (GetLabel(szBlackTeam, szLineStr, "BLACKTEAM")) { + } else if (GetLabel(szBlack, szLineStr, "BLACK")) { + } else if (GetLabel(szBlackElo, szLineStr, "BLACKELO")) { + } else if (GetLabel(szRem, szLineStr, "RESULT")) { + if (false) { + } else if (strcmp(szRem, "*") == 0) { + nResult = 0; + } else if (strcmp(szRem, "1-0") == 0) { + nResult = 1; + } else if (strcmp(szRem, "1/2-1/2") == 0) { + nResult = 2; + } else if (strcmp(szRem, "0-1") == 0) { + nResult = 3; + } else { + nResult = 0; + } + } else if (GetLabel(szEcco, szLineStr, "ECCO")) { + } else if (GetLabel(szOpen, szLineStr, "OPENING")) { + } else if (GetLabel(szVar, szLineStr, "VARIATION")) { + } else if (GetLabel(szRem, szLineStr, "FORMAT")) { + if (StrEqv(szRem, "WXF")) { + nNotation = 1; + } else if (StrEqv(szRem, "ICCS")) { + nNotation = 2; + } else { + nNotation = 0; + } + } else if (GetLabel(szRem, szLineStr, "FEN")) { + posStart.FromFen(szRem); + lpLineChar = strchr(szRem, '-'); + pos = posStart; + nCounter = 1; + } + bReturned = true; + } else { + bDetail = true; + } + } + } + if (!bNoAdvert) { + AppendStr(szCommentTable[nMaxMove], cszAdvertStr); + } + fclose(fp); + return true; +} + +inline void PrintLabel(FILE *fp, const char *szTag, const char *szValue) { + if (szValue[0] != '\0') { + fprintf(fp, "[%s \"%s\"]\r\n", szTag, szValue); + } +} + +bool PgnFileStruct::Write(const char *szFileName, bool bNoAdvert) const { + int i, nCounter, nStatus; + bool bReturned; + uint64_t dqChinMove; + char szFen[128]; + FILE *fp; + PositionStruct pos; + + fp = fopen(szFileName, "wb"); + if (fp == NULL) { + return false; + } + PrintLabel(fp, "Game", "Chinese Chess"); + PrintLabel(fp, "Event", szEvent); + PrintLabel(fp, "Round", szRound); + PrintLabel(fp, "Date", szDate); + PrintLabel(fp, "Site", szSite); + PrintLabel(fp, "RedTeam", szRedTeam); + PrintLabel(fp, "Red", szRed); + PrintLabel(fp, "RedElo", szRedElo); + PrintLabel(fp, "BlackTeam", szBlackTeam); + PrintLabel(fp, "Black", szBlack); + PrintLabel(fp, "BlackElo", szBlackElo); + PrintLabel(fp, "Result", cszResult[nResult]); + PrintLabel(fp, "ECCO", szEcco); + PrintLabel(fp, "Opening", szOpen); + PrintLabel(fp, "Variation", szVar); + posStart.ToFen(szFen); + if (strcmp(szFen, "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w") != 0) { + fprintf(fp, "[FEN \"%s - - 0 1\"]\r\n", szFen); + } + if (szCommentTable[0] != NULL) { + fprintf(fp, "{%s}\r\n", szCommentTable[0]); + } + pos = posStart; + bReturned = true; + nCounter = 1; + for (i = 1; i <= nMaxMove; i ++) { + if (bReturned) { + fprintf(fp, "%3d. ", nCounter); + if (pos.sdPlayer == 1) { + fprintf(fp, " "); + } + } + dqChinMove = File2Chin(Move2File(wmvMoveTable[i], pos), pos.sdPlayer); + if (pos.sdPlayer == 0) { + fprintf(fp, "%.8s", (const char *) &dqChinMove); + bReturned = false; + } else { + fprintf(fp, " %.8s\r\n", (const char *) &dqChinMove); + bReturned = true; + } + if (szCommentTable[i] != NULL) { + if (!bReturned) { + fprintf(fp, "\r\n"); + } + fprintf(fp, "{%s}\r\n", szCommentTable[i]); + bReturned = true; + } + TryMove(pos, nStatus, wmvMoveTable[i]); + if (pos.nMoveNum == MAX_MOVE_NUM) { + pos.SetIrrev(); + } + if (pos.sdPlayer == 0) { + nCounter ++; + if (nCounter == MAX_COUNTER) { + break; + } + } + } + fprintf(fp, " %s%s", cszResult[nResult], bNoAdvert ? "" : cszAdvertStr); + fclose(fp); + return true; +} diff --git a/cchess/pgnfile.h b/cchess/pgnfile.h new file mode 100644 index 0000000..226b959 --- /dev/null +++ b/cchess/pgnfile.h @@ -0,0 +1,56 @@ +/* +PGN->XQF - a Chinese Chess Score Convertion Program +Designed by Morning Yellow, Version: 2.1, Last Modified: Jun. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../cchess/cchess.h" + +#ifndef PGNFILE_H +#define PGNFILE_H + +const int MAX_STR_LEN = 256; +const int MAX_MOVE_LEN = 1999; +const int MAX_COUNTER = 1000; +const int MAX_REM_LEN = 4096; + +const bool NO_ADVERT = true; + +struct PgnFileStruct { + char szEvent[MAX_STR_LEN], szRound[MAX_STR_LEN], szDate[MAX_STR_LEN], szSite[MAX_STR_LEN]; + char szRedTeam[MAX_STR_LEN], szRed[MAX_STR_LEN], szRedElo[MAX_STR_LEN]; + char szBlackTeam[MAX_STR_LEN], szBlack[MAX_STR_LEN], szBlackElo[MAX_STR_LEN]; + char szEcco[MAX_STR_LEN], szOpen[MAX_STR_LEN], szVar[MAX_STR_LEN]; + int nMaxMove, nResult; + PositionStruct posStart; + unsigned short wmvMoveTable[MAX_MOVE_LEN]; + char *szCommentTable[MAX_MOVE_LEN]; + + void Init(void); + PgnFileStruct(void) { + Init(); + }; + ~PgnFileStruct(void); + void Reset(void) { + this->~PgnFileStruct(); + Init(); + } + bool Read(const char *szFileName, bool bNoAdvert = false); + bool Write(const char *szFileName, bool bNoAdvert = false) const; +}; // pgn + +#endif diff --git a/codec/BIG2GB/BIG2GB.BAS b/codec/BIG2GB/BIG2GB.BAS new file mode 100644 index 0000000..fca28f5 --- /dev/null +++ b/codec/BIG2GB/BIG2GB.BAS @@ -0,0 +1,46 @@ +Attribute VB_Name = "mdlBIG2GB" +Option Explicit + +Public Sub Main() + +Dim nFileIn As Integer, nFileOut As Integer, szFileName As String, szLineStr As String +If Command = "" Then + szFileName = OpenFileDialog("Open BIG File", "Text Files (*.TXT)|*.TXT|All Files (*.*)|*.*") +Else + If Left(Command, 1) = """" And Right(Command, 1) = """" Then + szFileName = Mid(Command, 2, Len(Command) - 2) + Else + szFileName = Command + End If +End If +If szFileName <> "" Then + nFileIn = FreeFile + On Error GoTo lnErrorOpen + Open szFileName For Input As #nFileIn + On Error GoTo 0 + nFileOut = FreeFile + Open szFileName + ".tmp" For Output As #nFileOut + Do While Not EOF(nFileIn) + Line Input #nFileIn, szLineStr + Print #nFileOut, frmHide.big2gb.BigToGB(szLineStr) + Loop + Close #nFileIn + Close #nFileOut + On Error GoTo lnErrorRename + Name szFileName As szFileName + ".bak" + On Error GoTo 0 + Name szFileName + ".tmp" As szFileName +End If +Unload frmHide +Exit Sub + +lnErrorOpen: +MsgBox "Cannot Open " + szFileName, vbExclamation +Unload frmHide +Exit Sub + +lnErrorRename: +Kill szFileName + ".bak" +Resume 0 + +End Sub diff --git a/codec/BIG2GB/BIG2GB.FRM b/codec/BIG2GB/BIG2GB.FRM new file mode 100644 index 0000000..115c084 --- /dev/null +++ b/codec/BIG2GB/BIG2GB.FRM @@ -0,0 +1,27 @@ +VERSION 5.00 +Object = "{B69BA6E9-1281-11D2-8C37-0060089950F4}#3.0#0"; "BIG2GB.OCX" +Begin VB.Form frmHide + BorderStyle = 3 'Fixed Dialog + ClientHeight = 3195 + ClientLeft = 45 + ClientTop = 330 + ClientWidth = 4680 + Icon = "BIG2GB.frx":0000 + MaxButton = 0 'False + MinButton = 0 'False + ScaleHeight = 3195 + ScaleWidth = 4680 + ShowInTaskbar = 0 'False + StartUpPosition = 3 'Windows Default + Begin BigToGB.Big2GB big2gb + Left = 0 + Top = 0 + _ExtentX = 423 + _ExtentY = 397 + End +End +Attribute VB_Name = "frmHide" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False diff --git a/codec/BIG2GB/BIG2GB.FRX b/codec/BIG2GB/BIG2GB.FRX new file mode 100644 index 0000000..c59ee6b Binary files /dev/null and b/codec/BIG2GB/BIG2GB.FRX differ diff --git a/codec/BIG2GB/BIG2GB.ICO b/codec/BIG2GB/BIG2GB.ICO new file mode 100644 index 0000000..8063916 Binary files /dev/null and b/codec/BIG2GB/BIG2GB.ICO differ diff --git a/codec/BIG2GB/BIG2GB.ISS b/codec/BIG2GB/BIG2GB.ISS new file mode 100644 index 0000000..ca09da5 --- /dev/null +++ b/codec/BIG2GB/BIG2GB.ISS @@ -0,0 +1,34 @@ +; Script generated by the Inno Setup Script Wizard. +; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! + +[CustomMessages] +Quot=" + +[Setup] +AppName=Big<=>GB Convertor +AppVerName=Big<=>GB Convertor +AppPublisher=www.elephantbase.net +AppPublisherURL=http://www.elephantbase.net/ +AppSupportURL=http://www.elephantbase.net/ +AppUpdatesURL=http://www.elephantbase.net/ +DefaultDirName={pf}\BIG2GB +DefaultGroupName=BIG2GB +OutputBaseFilename=big2gb +OutputDir=. +Compression=lzma +SolidCompression=yes + +[Files] +Source: "..\..\SYSTEM\BIG2GB.OCX"; DestDir: "{sys}"; Flags: regserver sharedfile uninsneveruninstall +Source: "..\..\BIN\BIG2GB.EXE"; DestDir: "{app}"; Flags: ignoreversion +Source: "..\..\BIN\GB2BIG.EXE"; DestDir: "{app}"; Flags: ignoreversion +Source: "..\..\BIN\BIGGBDLG.EXE"; DestDir: "{app}"; Flags: ignoreversion + +[Icons] +Name: "{group}\Big to GB Convertor"; Filename: "{app}\BIG2GB.EXE" +Name: "{group}\GB to BIG Convertor"; Filename: "{app}\GB2BIG.EXE" +Name: "{group}\Convertor Dialog Box"; Filename: "{app}\BIGGBDLG.EXE" +Name: "{group}\Remove BIG2GB"; Filename: "{uninstallexe}" +Name: "{userdesktop}\Big to GB Convertor"; Filename: "{app}\BIG2GB.EXE" +Name: "{userdesktop}\GB to Big Convertor"; Filename: "{app}\GB2BIG.EXE" +Name: "{userdesktop}\Convertor Dialog Box"; Filename: "{app}\BIGGBDLG.EXE" diff --git a/codec/BIG2GB/BIG2GB.VBP b/codec/BIG2GB/BIG2GB.VBP new file mode 100644 index 0000000..d000873 --- /dev/null +++ b/codec/BIG2GB/BIG2GB.VBP @@ -0,0 +1,40 @@ +Type=Exe +Form=BIG2GB.FRM +Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\WINDOWS\system32\stdole2.tlb#OLE Automation +Object={B69BA6E9-1281-11D2-8C37-0060089950F4}#3.0#0; BIG2GB.OCX +Module=mdlBIG2GB; BIG2GB.BAS +Module=mdlBase; ..\..\utility\BASE.BAS +IconForm="frmHide" +Startup="Sub Main" +HelpFile="" +Title="BIG2GB" +ExeName32="BIG2GB.EXE" +Path32="..\..\BIN" +Command32="" +Name="prjBig2GB" +HelpContextID="0" +CompatibleMode="0" +MajorVer=1 +MinorVer=0 +RevisionVer=0 +AutoIncrementVer=0 +ServerSupportFiles=0 +VersionCompanyName="Unknown Organization" +CompilationType=0 +OptimizationType=0 +FavorPentiumPro(tm)=0 +CodeViewDebugInfo=0 +NoAliasing=0 +BoundsCheck=0 +OverflowCheck=0 +FlPointCheck=0 +FDIVCheck=0 +UnroundedFP=0 +StartMode=0 +Unattended=0 +Retained=0 +ThreadPerObject=0 +MaxNumberOfThreads=1 + +[MS Transaction Server] +AutoRefresh=1 diff --git a/codec/BIG2GB/BIGGBDLG.FRM b/codec/BIG2GB/BIGGBDLG.FRM new file mode 100644 index 0000000..30061b2 --- /dev/null +++ b/codec/BIG2GB/BIGGBDLG.FRM @@ -0,0 +1,95 @@ +VERSION 5.00 +Object = "{B69BA6E9-1281-11D2-8C37-0060089950F4}#3.0#0"; "BIG2GB.OCX" +Begin VB.Form frmBigGBDlg + BorderStyle = 1 'Fixed Single + Caption = "Big<=>GB Convertor" + ClientHeight = 3195 + ClientLeft = 45 + ClientTop = 330 + ClientWidth = 4680 + BeginProperty Font + Name = "宋体" + Size = 9 + Charset = 134 + Weight = 400 + Underline = 0 'False + Italic = 0 'False + Strikethrough = 0 'False + EndProperty + Icon = "BIGGBDLG.frx":0000 + MaxButton = 0 'False + ScaleHeight = 3195 + ScaleWidth = 4680 + StartUpPosition = 3 'Windows Default + Begin VB.CommandButton btnExit + Cancel = -1 'True + Caption = "E&xit" + Height = 375 + Left = 1920 + TabIndex = 4 + Top = 2760 + Width = 855 + End + Begin VB.CommandButton btnGBToBig + Caption = "Big<-&GB" + Height = 375 + Left = 1920 + TabIndex = 3 + Top = 720 + Width = 855 + End + Begin VB.CommandButton btnBigToGB + Caption = "&Big->GB" + Height = 375 + Left = 1920 + TabIndex = 2 + Top = 120 + Width = 855 + End + Begin BigToGB.Big2GB big2gb + Left = 0 + Top = 0 + _ExtentX = 423 + _ExtentY = 397 + End + Begin VB.TextBox txtGB + Height = 3015 + Left = 2880 + MultiLine = -1 'True + ScrollBars = 3 'Both + TabIndex = 1 + Top = 120 + Width = 1695 + End + Begin VB.TextBox txtBig + Height = 3015 + Left = 120 + MultiLine = -1 'True + ScrollBars = 3 'Both + TabIndex = 0 + Top = 120 + Width = 1695 + End +End +Attribute VB_Name = "frmBigGBDlg" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False +Private Sub btnBigToGB_Click() + +txtGB.Text = Big2GB.BigToGB(txtBig.Text) + +End Sub + +Private Sub btnGBToBig_Click() + +txtBig.Text = Big2GB.GBToBig(txtGB.Text) + +End Sub + +Private Sub btnExit_Click() + +Unload frmBigGBDlg + +End Sub diff --git a/codec/BIG2GB/BIGGBDLG.FRX b/codec/BIG2GB/BIGGBDLG.FRX new file mode 100644 index 0000000..c0a3904 Binary files /dev/null and b/codec/BIG2GB/BIGGBDLG.FRX differ diff --git a/codec/BIG2GB/BIGGBDLG.ICO b/codec/BIG2GB/BIGGBDLG.ICO new file mode 100644 index 0000000..bf264a0 Binary files /dev/null and b/codec/BIG2GB/BIGGBDLG.ICO differ diff --git a/codec/BIG2GB/BIGGBDLG.VBP b/codec/BIG2GB/BIGGBDLG.VBP new file mode 100644 index 0000000..e121e1b --- /dev/null +++ b/codec/BIG2GB/BIGGBDLG.VBP @@ -0,0 +1,39 @@ +Type=Exe +Form=BIGGBDLG.FRM +Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#..\..\..\..\WINDOWS\SYSTEM\STDOLE2.TLB#OLE Automation +Object={B69BA6E9-1281-11D2-8C37-0060089950F4}#3.0#0; ..\..\SYSTEM\BIG2GB.OCX +IconForm="frmBigGBDlg" +Startup="frmBigGBDlg" +HelpFile="" +Title="BIGGBDLG" +ExeName32="BIGGBDLG.EXE" +Path32="..\..\BIN" +Command32="" +Name="prjBigGBDlg" +HelpContextID="0" +CompatibleMode="0" +MajorVer=1 +MinorVer=0 +RevisionVer=0 +AutoIncrementVer=0 +ServerSupportFiles=0 +VersionCompanyName="Unknown Organization" +CompilationType=0 +OptimizationType=0 +FavorPentiumPro(tm)=0 +CodeViewDebugInfo=0 +NoAliasing=0 +BoundsCheck=0 +OverflowCheck=0 +FlPointCheck=0 +FDIVCheck=0 +UnroundedFP=0 +StartMode=0 +Unattended=0 +Retained=0 +ThreadPerObject=0 +MaxNumberOfThreads=1 +DebugStartupOption=0 + +[MS Transaction Server] +AutoRefresh=1 diff --git a/codec/BIG2GB/GB2BIG.BAS b/codec/BIG2GB/GB2BIG.BAS new file mode 100644 index 0000000..6ba4fcd --- /dev/null +++ b/codec/BIG2GB/GB2BIG.BAS @@ -0,0 +1,46 @@ +Attribute VB_Name = "mdlGB2BIG" +Option Explicit + +Public Sub Main() + +Dim nFileIn As Integer, nFileOut As Integer, szFileName As String, szLineStr As String +If Command = "" Then + szFileName = OpenFileDialog("Open GB File", "Text Files (*.TXT)|*.TXT|All Files (*.*)|*.*") +Else + If Left(Command, 1) = """" And Right(Command, 1) = """" Then + szFileName = Mid(Command, 2, Len(Command) - 2) + Else + szFileName = Command + End If +End If +If szFileName <> "" Then + nFileIn = FreeFile + On Error GoTo lnErrorOpen + Open szFileName For Input As #nFileIn + On Error GoTo 0 + nFileOut = FreeFile + Open szFileName + ".tmp" For Output As #nFileOut + Do While Not EOF(nFileIn) + Line Input #nFileIn, szLineStr + Print #nFileOut, frmHide.big2gb.GBToBig(szLineStr) + Loop + Close #nFileIn + Close #nFileOut + On Error GoTo lnErrorRename + Name szFileName As szFileName + ".bak" + On Error GoTo 0 + Name szFileName + ".tmp" As szFileName +End If +Unload frmHide +Exit Sub + +lnErrorOpen: +MsgBox "Cannot Open " + szFileName, vbExclamation +Unload frmHide +Exit Sub + +lnErrorRename: +Kill szFileName + ".bak" +Resume 0 + +End Sub diff --git a/codec/BIG2GB/GB2BIG.FRM b/codec/BIG2GB/GB2BIG.FRM new file mode 100644 index 0000000..4d9ed5c --- /dev/null +++ b/codec/BIG2GB/GB2BIG.FRM @@ -0,0 +1,27 @@ +VERSION 5.00 +Object = "{B69BA6E9-1281-11D2-8C37-0060089950F4}#3.0#0"; "BIG2GB.OCX" +Begin VB.Form frmHide + BorderStyle = 3 'Fixed Dialog + ClientHeight = 3195 + ClientLeft = 45 + ClientTop = 330 + ClientWidth = 4680 + Icon = "GB2BIG.frx":0000 + MaxButton = 0 'False + MinButton = 0 'False + ScaleHeight = 3195 + ScaleWidth = 4680 + ShowInTaskbar = 0 'False + StartUpPosition = 3 'Windows Default + Begin BigToGB.Big2GB big2gb + Left = 0 + Top = 0 + _ExtentX = 423 + _ExtentY = 397 + End +End +Attribute VB_Name = "frmHide" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False diff --git a/codec/BIG2GB/GB2BIG.FRX b/codec/BIG2GB/GB2BIG.FRX new file mode 100644 index 0000000..1ffe26a Binary files /dev/null and b/codec/BIG2GB/GB2BIG.FRX differ diff --git a/codec/BIG2GB/GB2BIG.ICO b/codec/BIG2GB/GB2BIG.ICO new file mode 100644 index 0000000..ac9d8af Binary files /dev/null and b/codec/BIG2GB/GB2BIG.ICO differ diff --git a/codec/BIG2GB/GB2BIG.VBP b/codec/BIG2GB/GB2BIG.VBP new file mode 100644 index 0000000..ef51603 --- /dev/null +++ b/codec/BIG2GB/GB2BIG.VBP @@ -0,0 +1,40 @@ +Type=Exe +Form=GB2BIG.FRM +Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\WINDOWS\system32\stdole2.tlb#OLE Automation +Object={B69BA6E9-1281-11D2-8C37-0060089950F4}#3.0#0; BIG2GB.OCX +Module=mdlGB2BIG; GB2BIG.BAS +Module=mdlBase; ..\..\utility\BASE.BAS +IconForm="frmHide" +Startup="Sub Main" +HelpFile="" +Title="GB2BIG" +ExeName32="GB2BIG.EXE" +Path32="..\..\BIN" +Command32="" +Name="prjGB2Big" +HelpContextID="0" +CompatibleMode="0" +MajorVer=1 +MinorVer=0 +RevisionVer=0 +AutoIncrementVer=0 +ServerSupportFiles=0 +VersionCompanyName="Unknown Organization" +CompilationType=0 +OptimizationType=0 +FavorPentiumPro(tm)=0 +CodeViewDebugInfo=0 +NoAliasing=0 +BoundsCheck=0 +OverflowCheck=0 +FlPointCheck=0 +FDIVCheck=0 +UnroundedFP=0 +StartMode=0 +Unattended=0 +Retained=0 +ThreadPerObject=0 +MaxNumberOfThreads=1 + +[MS Transaction Server] +AutoRefresh=1 diff --git a/codec/base64/BASE64.BAS b/codec/base64/BASE64.BAS new file mode 100644 index 0000000..b9f889e --- /dev/null +++ b/codec/base64/BASE64.BAS @@ -0,0 +1,3 @@ +Attribute VB_Name = "mdlBase64" +Public Declare Sub Base64Enc Lib "BASE64.DLL" Alias "_Base64Enc@16" (ByVal lpText As Long, ByVal lpData As Long, ByVal nBlockLen As Long, ByVal nLineLen As Long) +Public Declare Function Base64Dec Lib "BASE64.DLL" Alias "_Base64Dec@8" (ByVal lpData As Long, ByVal lpText As Long) As Long diff --git a/codec/base64/BASE64.FRM b/codec/base64/BASE64.FRM new file mode 100644 index 0000000..6ef4120 --- /dev/null +++ b/codec/base64/BASE64.FRM @@ -0,0 +1,207 @@ +VERSION 5.00 +Begin VB.Form frmBase64 + BorderStyle = 1 'Fixed Single + Caption = "Base64 Codec" + ClientHeight = 3195 + ClientLeft = 45 + ClientTop = 330 + ClientWidth = 4680 + BeginProperty Font + Name = "宋体" + Size = 9 + Charset = 134 + Weight = 400 + Underline = 0 'False + Italic = 0 'False + Strikethrough = 0 'False + EndProperty + MaxButton = 0 'False + OLEDropMode = 1 'Manual + ScaleHeight = 3195 + ScaleWidth = 4680 + StartUpPosition = 3 'Windows Default + Begin VB.CommandButton btnLoad + Caption = "Load" + Height = 375 + Left = 1440 + TabIndex = 3 + Top = 2760 + Width = 855 + End + Begin VB.TextBox txtCode + Height = 2535 + Left = 120 + MultiLine = -1 'True + OLEDropMode = 1 'Manual + ScrollBars = 2 'Vertical + TabIndex = 0 + Top = 120 + Width = 4455 + End + Begin VB.CheckBox chkMultiLine + Caption = "MultiLine" + Height = 255 + Left = 240 + TabIndex = 2 + Top = 2880 + Width = 1215 + End + Begin VB.CheckBox chkClipboard + Caption = "Clipboard" + Height = 255 + Left = 240 + TabIndex = 1 + Top = 2640 + Width = 1215 + End + Begin VB.CommandButton btnExit + Cancel = -1 'True + Caption = "Exit" + Height = 375 + Left = 3360 + TabIndex = 5 + Top = 2760 + Width = 855 + End + Begin VB.CommandButton btnSave + Caption = "Save" + Height = 375 + Left = 2400 + TabIndex = 4 + Top = 2760 + Width = 855 + End +End +Attribute VB_Name = "frmBase64" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False +Option Explicit + +Private Declare Function lstrlenA Lib "KERNEL32.DLL" (ByVal lpString As Long) As Long + +Private Sub ReadFile(ByVal szFileName As String) + +Dim i As Long, nFileLen As Long, nOpenFile As Integer +nOpenFile = FreeFile +On Error GoTo lnErrorOpen +Open szFileName For Binary Access Read As #nOpenFile +On Error GoTo 0 +nFileLen = LOF(nOpenFile) +ReDim ucData(1 To nFileLen) As Byte +ReDim ucText(1 To (nFileLen \ 33 + 1) * 46) As Byte +For i = 1 To nFileLen + Get #nOpenFile, , ucData(i) +Next +Base64Enc VarPtr(ucText(1)), VarPtr(ucData(1)), nFileLen, chkMultiLine.Value * 11 +txtCode.Text = MkBStr(VarPtr(ucText(1))) +Erase ucText +Erase ucData +Close #nOpenFile + +Exit Sub +lnErrorOpen: +MsgBox "Cannot Read File " + szFileName, vbExclamation +On Error GoTo 0 + +End Sub + +Private Sub ReadClipboard(ByVal sz As String) + +Dim i As Long, nFileLen As Long + +nFileLen = lstrlenA(StrPtr(sz)) +ReDim ucText(1 To (nFileLen \ 33 + 1) * 46) As Byte +Base64Enc VarPtr(ucText(1)), StrPtr(sz), nFileLen, chkMultiLine.Value * 11 +txtCode.Text = MkBStr(VarPtr(ucText(1))) +Erase ucText + +End Sub + +Private Sub Form_Load() + +Dim sz As String +sz = Command +If sz <> "" Then + If Left(sz, 1) = """" And Right(sz, 1) = """" Then + sz = Mid(sz, 2, Len(sz) - 2) + End If + ReadFile sz +End If + +End Sub + +Private Sub Form_OLEDragDrop(data As DataObject, effect As Long, button As Integer, shift As Integer, x As Single, y As Single) + +If data.GetFormat(vbCFFiles) Then + ReadFile data.Files(1) +End If + +End Sub + +Private Sub btnLoad_Click() + +Dim sz As String +Dim i As Long, nFileLen As Long, nOpenFile As Integer +If chkClipboard.Value = 0 Then + sz = OpenFileDialog("Load Binary File", "Binary File (*.*)|*.*") + If sz <> "" Then + ReadFile sz + End If +Else + sz = Clipboard.GetText + If sz <> "" Then + ReadClipboard sz + End If +End If + +End Sub + +Private Sub btnSave_Click() + +Dim i As Long, nLen As Long, nOpenFile As Integer, sz As String +If chkClipboard.Value = 0 Then + sz = SaveFileDialog("Load Binary File", "Binary File (*.*)|*.*", "") + If sz <> "" Then + nOpenFile = FreeFile + On Error GoTo lnErrorOpen + Open sz For Binary Access Write As #nOpenFile + On Error GoTo 0 + ReDim ucData(1 To (Len(txtCode.Text) \ 4 + 1) * 3) As Byte + nLen = Base64Dec(VarPtr(ucData(1)), CvBStr("" + txtCode.Text)) + For i = 1 To nLen + Put #nOpenFile, , ucData(i) + Next + Erase ucData + Close #nOpenFile + End If +Else + ReDim ucData(0 To (Len(txtCode.Text) \ 4 + 1) * 3) As Byte + nLen = Base64Dec(VarPtr(ucData(0)), CvBStr("" + txtCode.Text)) + ucData(nLen) = 0 + Clipboard.Clear + Clipboard.SetText MkBStr(VarPtr(ucData(0))) + Erase ucData +End If + +Exit Sub +lnErrorOpen: +MsgBox "Cannot Write File " + sz, vbExclamation +On Error GoTo 0 + +End Sub + +Private Sub btnExit_Click() + +Unload Me + +End Sub + +Private Sub txtCode_OLEDragDrop(data As DataObject, effect As Long, button As Integer, shift As Integer, x As Single, y As Single) + +If data.GetFormat(vbCFFiles) Then + ReadFile data.Files(1) +End If + +End Sub diff --git a/codec/base64/BASE64.VBP b/codec/base64/BASE64.VBP new file mode 100644 index 0000000..7031778 --- /dev/null +++ b/codec/base64/BASE64.VBP @@ -0,0 +1,45 @@ +Type=Exe +Form=BASE64.FRM +Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\WINDOWS\system32\stdole2.tlb#OLE Automation +Module=mdlBase64; BASE64.BAS +Module=mdlBase; ..\..\utility\BASE.BAS +IconForm="frmBase64" +Startup="frmBase64" +HelpFile="" +Title="Base64 Codec" +ExeName32="BASE64.EXE" +Path32="..\..\BIN" +Command32="" +Name="prjBase64" +HelpContextID="0" +CompatibleMode="0" +MajorVer=1 +MinorVer=0 +RevisionVer=0 +AutoIncrementVer=0 +ServerSupportFiles=0 +VersionComments="Base64 Codec, (C) 2004-2006 www.elephantbase.net" +VersionCompanyName="Author: Morning Yellow (Address: Shanghai Computer Chess Research Center, eMail: webmaster@elephantbase.net)" +VersionFileDescription="Base64 Codec" +VersionLegalCopyright="Base64 Codec, (C) 2004-2006 www.elephantbase.net" +VersionLegalTrademarks="www.elephantbase.net" +VersionProductName="Base64 Codec" +CompilationType=0 +OptimizationType=0 +FavorPentiumPro(tm)=0 +CodeViewDebugInfo=0 +NoAliasing=0 +BoundsCheck=0 +OverflowCheck=0 +FlPointCheck=0 +FDIVCheck=0 +UnroundedFP=0 +StartMode=0 +Unattended=0 +Retained=0 +ThreadPerObject=0 +MaxNumberOfThreads=1 +DebugStartupOption=0 + +[MS Transaction Server] +AutoRefresh=1 diff --git a/codec/base64/MAKEFILE.BAT b/codec/base64/MAKEFILE.BAT new file mode 100644 index 0000000..97ea56e --- /dev/null +++ b/codec/base64/MAKEFILE.BAT @@ -0,0 +1,7 @@ +@ECHO OFF +CL /O2 /W3 /Fe..\..\BIN\B64ENC.EXE B64ENC.C +CL /O2 /W3 /Fe..\..\BIN\B64DEC.EXE B64DEC.C +CL /O2 /W3 /LD /DBASE64_DLL /Fe..\..\BIN\BASE64.DLL BASE64.CPP +DEL *.OBJ +DEL ..\..\BIN\*.LIB +DEL ..\..\BIN\*.EXP \ No newline at end of file diff --git a/codec/base64/b64dec.c b/codec/base64/b64dec.c new file mode 100644 index 0000000..ca6ed32 --- /dev/null +++ b/codec/base64/b64dec.c @@ -0,0 +1,67 @@ +#include +#include + +#define LINE_INPUT_MAX_CHAR 1024 + +const char cszDecTab[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +int main(int argc, char **argv) { + int nCounter, nAsc0, nAsc[4]; + FILE *fp; + unsigned char ucBlock[4]; + char *lpLineStr; + char szLineStr[LINE_INPUT_MAX_CHAR]; + if (argc == 1) { + printf("=== Base64 Decoding Program ===\n"); + printf("Usage: B64DEC Binary-File < Base64-File\n"); + } else { + fp = fopen(argv[1], "wb"); + if (fp == NULL) { + printf("Unable to Create File: %s\n", argv[1]); + } else { + nCounter = 0; + while (fgets(szLineStr, LINE_INPUT_MAX_CHAR, stdin) != NULL) { + lpLineStr = szLineStr; + while (*lpLineStr != '\0') { + nAsc0 = cszDecTab[(int) *lpLineStr]; + if (nAsc0 != -1) { + nAsc[nCounter] = nAsc0; + nCounter ++; + if (nCounter == 4) { + ucBlock[0] = (nAsc[0] << 2) | (nAsc[1] >> 4); + ucBlock[1] = ((nAsc[1] & 15) << 4) | (nAsc[2] >> 2); + ucBlock[2] = ((nAsc[2] & 3) << 6) | nAsc[3]; + fwrite(ucBlock, 3, 1, fp); + nCounter = 0; + } + } + lpLineStr ++; + } + } + if (nCounter > 1) { + ucBlock[0] = (nAsc[0] << 2) | (nAsc[1] >> 4); + ucBlock[1] = ((nAsc[1] & 15) << 4) | (nAsc[2] >> 2); + fwrite(ucBlock, nCounter - 1, 1, fp); + } + fclose(fp); + } + } + return 0; +} diff --git a/codec/base64/b64enc.c b/codec/base64/b64enc.c new file mode 100644 index 0000000..b550a54 --- /dev/null +++ b/codec/base64/b64enc.c @@ -0,0 +1,47 @@ +#include + +const char *const cszEncTab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +int main(int argc, char **argv) { + long i, nFileLen, nBlocks; + unsigned char ucBlock[4]; + FILE *fp; + if (argc == 1) { + printf("=== Base64 Encoding Program ===\n"); + printf("Usage: B64ENC Binary-File > Base64-File\n"); + } else { + fp = fopen(argv[1], "rb"); + if (fp == NULL) { + printf("Unable to Open File: %s\n", argv[1]); + } else { + fseek(fp, 0, SEEK_END); + nFileLen = ftell(fp); + fseek(fp, 0, SEEK_SET); + nBlocks = nFileLen / 3; + for (i = 0; i < nBlocks; i ++) { + fread(ucBlock, 3, 1, fp); + putchar(cszEncTab[ucBlock[0] >> 2]); + putchar(cszEncTab[((ucBlock[0] & 3) << 4) + (ucBlock[1] >> 4)]); + putchar(cszEncTab[((ucBlock[1] & 15) << 2) + (ucBlock[2] >> 6)]); + putchar(cszEncTab[ucBlock[2] & 63]); + if ((i + 1) % 19 == 0) { + printf("\n"); + } + } + if (nFileLen % 3 > 0) { + fread(ucBlock, 3, 1, fp); + putchar(cszEncTab[ucBlock[0] >> 2]); + if (nFileLen % 3 > 1) { + putchar(cszEncTab[((ucBlock[0] & 3) << 4) + (ucBlock[1] >> 4)]); + putchar(cszEncTab[(ucBlock[1] & 15) << 2]); + } else { + putchar(cszEncTab[(ucBlock[0] & 3) << 4]); + putchar('='); + } + putchar('='); + } + fclose(fp); + } + } + return 0; +} diff --git a/codec/base64/base64.cpp b/codec/base64/base64.cpp new file mode 100644 index 0000000..86af52e --- /dev/null +++ b/codec/base64/base64.cpp @@ -0,0 +1,119 @@ +#ifdef BASE64_DLL + +#include +#include "base64.h" + +extern "C" __declspec(dllexport) VOID WINAPI Base64Enc(LPSTR szAsc, LPVOID lpBlock, LONG nBlockLen, LONG nLineLen); +extern "C" __declspec(dllexport) LONG WINAPI Base64Dec(LPVOID lpBlock, LPCSTR szAsc); + +VOID WINAPI Base64Enc(LPSTR szAsc, LPVOID lpBlock, LONG nBlockLen, LONG nLineLen) { + B64Enc(szAsc, lpBlock, nBlockLen, nLineLen); +} + +LONG WINAPI Base64Dec(LPVOID lpBlock, LPCSTR szAsc) { + return B64Dec(lpBlock, szAsc); +} + +#endif + +const char *const cszEncTab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +const char cszDecTab[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +void B64Enc(char *szAsc, void *lpBlock, int nBlockLen, int nLineLen) { + char *lpAsc; + unsigned char *lpuc; + int i, nBlocks; + lpAsc = szAsc; + lpuc = (unsigned char *) lpBlock; + nBlocks = nBlockLen / 3; + for (i = 0; i < nBlocks; i ++) { + *lpAsc = cszEncTab[lpuc[0] >> 2]; + lpAsc ++; + *lpAsc = cszEncTab[((lpuc[0] & 3) << 4) + (lpuc[1] >> 4)]; + lpAsc ++; + *lpAsc = cszEncTab[((lpuc[1] & 15) << 2) + (lpuc[2] >> 6)]; + lpAsc ++; + *lpAsc = cszEncTab[lpuc[2] & 63]; + lpAsc ++; + if (nLineLen > 0) { + if ((i + 1) % nLineLen == 0) { + *lpAsc = '\r'; + lpAsc ++; + *lpAsc = '\n'; + lpAsc ++; + } + } + lpuc += 3; + } + if (nBlockLen % 3 > 0) { + *lpAsc = cszEncTab[lpuc[0] >> 2]; + lpAsc ++; + if (nBlockLen % 3 > 1) { + *lpAsc = cszEncTab[((lpuc[0] & 3) << 4) + (lpuc[1] >> 4)]; + lpAsc ++; + *lpAsc = cszEncTab[(lpuc[1] & 15) << 2]; + lpAsc ++; + } else { + *lpAsc = cszEncTab[(lpuc[0] & 3) << 4]; + lpAsc ++; + *lpAsc = '='; + lpAsc ++; + } + *lpAsc = '='; + lpAsc ++; + } + *lpAsc = '\0'; +} + +int B64Dec(void *lpBlock, const char *szAsc) { + int nCounter, nAsc0, nAsc[4]; + const char *lpAsc; + unsigned char *lpuc; + lpuc = (unsigned char *) lpBlock; + nCounter = 0; + lpAsc = szAsc; + while (*lpAsc != '\0') { + nAsc0 = cszDecTab[(int) *lpAsc]; + if (nAsc0 != -1) { + nAsc[nCounter] = nAsc0; + nCounter ++; + if (nCounter == 4) { + *lpuc = (nAsc[0] << 2) | (nAsc[1] >> 4); + lpuc ++; + *lpuc = ((nAsc[1] & 15) << 4) | (nAsc[2] >> 2); + lpuc ++; + *lpuc = ((nAsc[2] & 3) << 6) | nAsc[3]; + lpuc ++; + nCounter = 0; + } + } + lpAsc ++; + } + if (nCounter > 1) { + *lpuc = (nAsc[0] << 2) | (nAsc[1] >> 4); + lpuc ++; + if (nCounter > 2) { + *lpuc = ((nAsc[1] & 15) << 4) | (nAsc[2] >> 2); + lpuc ++; + } + } + return lpuc - (unsigned char *) lpBlock; +} diff --git a/codec/base64/base64.h b/codec/base64/base64.h new file mode 100644 index 0000000..76eade7 --- /dev/null +++ b/codec/base64/base64.h @@ -0,0 +1,7 @@ +#ifndef BASE64_H +#define BASE64_H + +void B64Enc(char *szAsc, void *lpBlock, int nBlockLen, int nLineLen); +int B64Dec(void *lpBlock, const char *szAsc); + +#endif diff --git a/codec/crc32/CRC32.BAS b/codec/crc32/CRC32.BAS new file mode 100644 index 0000000..73a77d2 --- /dev/null +++ b/codec/crc32/CRC32.BAS @@ -0,0 +1,22 @@ +Attribute VB_Name = "mdlCrc32" +Public Declare Sub Crc32Init Lib "CRC32.DLL" Alias "_Crc32Init@0" () +Public Declare Sub Crc32Reset Lib "CRC32.DLL" Alias "_Crc32Reset@4" (ByRef dwCrc32 As Long) +Public Declare Sub Crc32Update Lib "CRC32.DLL" Alias "_Crc32Update@12" (ByRef dwCrc32 As Long, ByVal lpsz As String, ByVal nLen As Long) +Public Declare Function Crc32Digest Lib "CRC32.DLL" Alias "_Crc32Digest@4" (ByRef dwCrc32 As Long) As Long + +Private Declare Function lstrlenA Lib "KERNEL32.DLL" (ByVal lpsz As String) As Long + +Private bInit As Boolean + +Public Function Crc32Hex(ByVal sz As String) As String + +Dim dwCrc32 As Long, szHex As String +If Not bInit Then + Crc32Init +End If +Crc32Reset dwCrc32 +Crc32Update dwCrc32, sz, lstrlenA(sz) +szHex = Hex(Crc32Digest(dwCrc32)) +Crc32Hex = String(8 - Len(szHex), "0") + szHex + +End Function diff --git a/codec/crc32/crc32.cpp b/codec/crc32/crc32.cpp new file mode 100644 index 0000000..5809cf1 --- /dev/null +++ b/codec/crc32/crc32.cpp @@ -0,0 +1,52 @@ +#include "../../base/base.h" +#include "crc32.h" + +#ifdef CRC32_DLL + +#include + +extern "C" __declspec(dllexport) VOID WINAPI Crc32Init(VOID); +extern "C" __declspec(dllexport) VOID WINAPI Crc32Reset(LPDWORD lpdwCrc); +extern "C" __declspec(dllexport) VOID WINAPI Crc32Update(LPDWORD lpdwCrc, LPCSTR lpBuffer, LONG nLen); +extern "C" __declspec(dllexport) DWORD WINAPI Crc32Digest(LPDWORD lpdwCrc); + +VOID WINAPI Crc32Init(VOID) { + InitCrc32Table(); +} + +VOID WINAPI Crc32Reset(LPDWORD lpdwCrc) { + ((Crc32 *) lpdwCrc)->Reset(); +} + +VOID WINAPI Crc32Update(LPDWORD lpdwCrc, LPCSTR lpBuffer, LONG nLen) { + ((Crc32 *) lpdwCrc)->Update((const uint8_t *) lpBuffer, nLen); +} + +DWORD WINAPI Crc32Digest(LPDWORD lpdwCrc) { + return ((Crc32 *) lpdwCrc)->Digest(); +} + +#endif + +static const uint32_t CRC32_IV = 0xedb88320; + +uint32_t dwCrc32Table[256]; + +void InitCrc32Table(void) { + int i, j; + uint32_t r; + for (i = 0; i < 256; i ++) { + r = i; + for (j = 0; j < 8; j ++) { + r = (r >> 1) ^ ((r & 1) == 0 ? 0 : CRC32_IV); + } + dwCrc32Table[i] = r; + } +} + +void Crc32::Update(const uint8_t *lpuc, int nLen) { + int i; + for (i = 0; i < nLen; i ++) { + Update(lpuc[i]); + } +} diff --git a/codec/crc32/crc32.h b/codec/crc32/crc32.h new file mode 100644 index 0000000..607bb58 --- /dev/null +++ b/codec/crc32/crc32.h @@ -0,0 +1,24 @@ +#include "../../base/base.h" + +#ifndef CRC32_H +#define CRC32_H + +extern uint32_t dwCrc32Table[256]; + +struct Crc32 { + uint32_t dwCrc32; + void Reset(void) { + dwCrc32 = ~0; + } + void Update(uint8_t uc) { + dwCrc32 = dwCrc32Table[(uint8_t) dwCrc32 ^ uc] ^ (dwCrc32 >> 8); + } + void Update(const uint8_t *lpuc, int nLen); + uint32_t Digest(void) { + return ~dwCrc32; + } +}; + +void InitCrc32Table(void); + +#endif diff --git a/codec/dumphex/DUMPHEX.BAS b/codec/dumphex/DUMPHEX.BAS new file mode 100644 index 0000000..af1412c --- /dev/null +++ b/codec/dumphex/DUMPHEX.BAS @@ -0,0 +1,2 @@ +Attribute VB_Name = "mdlDumpHex" +Public Declare Sub DumpHex Lib "DUMPHEX.DLL" Alias "_DumpHexA@16" (ByVal lpText As Long, ByVal lpData As Long, ByVal nOffset As Long, ByVal nBlockLen As Long, ByVal nLength As Long) diff --git a/codec/dumphex/dumphex.cpp b/codec/dumphex/dumphex.cpp new file mode 100644 index 0000000..3bb0f0a --- /dev/null +++ b/codec/dumphex/dumphex.cpp @@ -0,0 +1,65 @@ +#include + +#ifdef DUMPHEX_DLL + +#include +#include "dumphex.h" + +extern "C" __declspec(dllexport) VOID WINAPI DumpHexA(LPSTR szHexText, LPCSTR lpBuffer, LONG nOffset, LONG nLength); + +VOID WINAPI DumpHexA(LPSTR szHexText, LPCSTR lpBuffer, LONG nOffset, LONG nLength) { + DumpHex(szHexText, lpBuffer, nOffset, nLength); +} + +#endif + +static const char *const cszHexChar = "0123456789ABCDEF"; + +static int DumpLine(char *szHexText, const char *lpBuffer, int nOffsetDiv16, int nBegin, int nEnd) { + int i, nByte; + + memset(szHexText, ' ', 77); + szHexText[77] = '\r'; + szHexText[78] = '\n'; + szHexText[0] = cszHexChar[(nOffsetDiv16 >> 24) & 0xf]; + szHexText[1] = cszHexChar[(nOffsetDiv16 >> 20) & 0xf]; + szHexText[2] = cszHexChar[(nOffsetDiv16 >> 16) & 0xf]; + szHexText[3] = cszHexChar[(nOffsetDiv16 >> 12) & 0xf]; + szHexText[4] = ':'; + szHexText[5] = cszHexChar[(nOffsetDiv16 >> 8) & 0xf]; + szHexText[6] = cszHexChar[(nOffsetDiv16 >> 4) & 0xf]; + szHexText[7] = cszHexChar[nOffsetDiv16 & 0xf]; + szHexText[8] = '0'; + for (i = 0; i < 16; i ++) { + if (i >= nBegin && i < nEnd) { + nByte = lpBuffer[nOffsetDiv16 * 16 + i]; + szHexText[i * 3 + 11] = cszHexChar[(nByte & 0xf0) >> 4]; + szHexText[i * 3 + 12] = cszHexChar[nByte & 0xf]; + szHexText[61 + i] = (nByte >= 32 && nByte < 127 ? nByte : '.'); + } + } + if (nBegin < 8 && nEnd > 8) { + szHexText[34] = '-'; + } + return 79; +} + +void DumpHex(char *szHexText, const char *lpBuffer, int nOffset, int nLength) { + int i, nOffsetDiv16, nEnd, nEndDiv16, nTextLen; + + nEnd = nOffset + nLength; + nOffsetDiv16 = nOffset / 16; + nEndDiv16 = nEnd / 16; + if (nOffsetDiv16 == nEndDiv16) { + nTextLen = DumpLine(szHexText, lpBuffer, nOffsetDiv16, nOffset % 16, nEnd % 16); + } else { + nTextLen = DumpLine(szHexText, lpBuffer, nOffsetDiv16, nOffset % 16, 16); + for (i = nOffsetDiv16 + 1; i < nEndDiv16; i ++) { + nTextLen += DumpLine(szHexText + nTextLen, lpBuffer, i, 0, 16); + } + if (nEnd % 16 > 0) { + nTextLen += DumpLine(szHexText + nTextLen, lpBuffer, nEndDiv16, 0, nEnd % 16); + } + } + szHexText[nTextLen] = '\0'; +} \ No newline at end of file diff --git a/codec/dumphex/dumphex.h b/codec/dumphex/dumphex.h new file mode 100644 index 0000000..7fb534d --- /dev/null +++ b/codec/dumphex/dumphex.h @@ -0,0 +1,6 @@ +#ifndef DUMPHEX_H +#define DUMPHEX_H + +void DumpHex(char *szHexText, const char *lpBuffer, int nOffset, int nLength); + +#endif diff --git a/codec/unixtext/UNIXTEXT.FRM b/codec/unixtext/UNIXTEXT.FRM new file mode 100644 index 0000000..14c30a7 --- /dev/null +++ b/codec/unixtext/UNIXTEXT.FRM @@ -0,0 +1,186 @@ +VERSION 5.00 +Begin VB.Form frmUnixText + BorderStyle = 1 'Fixed Single + Caption = "Unix Text Generator" + ClientHeight = 3195 + ClientLeft = 45 + ClientTop = 330 + ClientWidth = 4680 + BeginProperty Font + Name = "宋体" + Size = 9 + Charset = 134 + Weight = 400 + Underline = 0 'False + Italic = 0 'False + Strikethrough = 0 'False + EndProperty + MaxButton = 0 'False + OLEDropMode = 1 'Manual + ScaleHeight = 3195 + ScaleWidth = 4680 + StartUpPosition = 3 'Windows Default + Begin VB.CommandButton btnLoad + Caption = "Load" + Height = 375 + Left = 1320 + TabIndex = 3 + Top = 2760 + Width = 855 + End + Begin VB.CommandButton btnSave + Caption = "Save" + Height = 375 + Left = 2400 + TabIndex = 4 + Top = 2760 + Width = 855 + End + Begin VB.CheckBox chkMacText + Caption = "Mac Text" + Height = 255 + Left = 120 + TabIndex = 2 + Top = 2880 + Width = 1215 + End + Begin VB.TextBox txtText + Height = 2535 + Left = 120 + MultiLine = -1 'True + OLEDropMode = 1 'Manual + ScrollBars = 3 'Both + TabIndex = 0 + Top = 120 + Width = 4455 + End + Begin VB.CheckBox chkClipboard + Caption = "Clipboard" + Height = 255 + Left = 120 + TabIndex = 1 + Top = 2640 + Width = 1215 + End + Begin VB.CommandButton btnExit + Cancel = -1 'True + Caption = "Exit" + Height = 375 + Left = 3480 + TabIndex = 5 + Top = 2760 + Width = 855 + End +End +Attribute VB_Name = "frmUnixText" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False +Option Explicit + +Private Sub ReadFile(ByVal szFileName As String) + +Dim sz As String, szLine As String, nOpenFile As Integer +nOpenFile = FreeFile +On Error GoTo lnErrorOpen +Open szFileName For Input As #nOpenFile +On Error GoTo 0 +sz = "" +Do While Not EOF(nOpenFile) + Line Input #nOpenFile, szLine + szLine = Replace(szLine, IIf(chkMacText.Value = 0, Chr(10), Chr(13)), vbCrLf) + sz = sz + szLine + vbCrLf +Loop +txtText.Text = sz +Close #nOpenFile + +Exit Sub +lnErrorOpen: +MsgBox "Cannot Read File " + szFileName, vbExclamation +On Error GoTo 0 + +End Sub + +Private Sub ReadClipboard(ByVal sz As String) + +txtText.Text = Replace(sz, IIf(chkMacText.Value = 0, Chr(10), Chr(13)), vbCrLf) + +End Sub + +Private Sub Form_Load() + +Dim sz As String +sz = Command +If sz <> "" Then + If Left(sz, 1) = """" And Right(sz, 1) = """" Then + sz = Mid(sz, 2, Len(sz) - 2) + End If + ReadFile sz +End If + +End Sub + +Private Sub Form_OLEDragDrop(data As DataObject, effect As Long, button As Integer, shift As Integer, x As Single, y As Single) + +If data.GetFormat(vbCFFiles) Then + ReadFile data.Files(1) +End If + +End Sub + +Private Sub btnLoad_Click() + +Dim sz As String, szLine As String, nOpenFile As Integer +If chkClipboard.Value = 0 Then + sz = OpenFileDialog("Load Text File", "Text File (*.*)|*.*") + If sz <> "" Then + ReadFile sz + End If +Else + sz = Clipboard.GetText + If sz <> "" Then + ReadClipboard sz + End If +End If + +End Sub + +Private Sub btnSave_Click() + +Dim nLen As Long, nOpenFile As Integer, sz As String +If chkClipboard.Value = 0 Then + sz = SaveFileDialog("Save Text File", "Text File (*.*)|*.*", "") + If sz <> "" Then + nOpenFile = FreeFile + On Error GoTo lnErrorOpen + Open sz For Output As #nOpenFile + On Error GoTo 0 + Print #nOpenFile, Replace(txtText.Text, IIf(chkMacText.Value = 0, Chr(13), Chr(10)), ""); + Close #nOpenFile + End If +Else + Clipboard.Clear + Clipboard.SetText Replace(txtText.Text, IIf(chkMacText.Value = 0, Chr(13), Chr(10)), "") +End If + +Exit Sub +lnErrorOpen: +MsgBox "Cannot Write File " + sz, vbExclamation +On Error GoTo 0 + +End Sub + +Private Sub btnExit_Click() + +Unload Me + +End Sub + +Private Sub txtText_OLEDragDrop(data As DataObject, effect As Long, button As Integer, shift As Integer, x As Single, y As Single) + +If data.GetFormat(vbCFFiles) Then + ReadFile data.Files(1) +End If + +End Sub diff --git a/codec/unixtext/UNIXTEXT.VBP b/codec/unixtext/UNIXTEXT.VBP new file mode 100644 index 0000000..060b0fd --- /dev/null +++ b/codec/unixtext/UNIXTEXT.VBP @@ -0,0 +1,39 @@ +Type=Exe +Form=UNIXTEXT.FRM +Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\WINDOWS\system32\stdole2.tlb#OLE Automation +Module=mdlBase; ..\..\utility\BASE.BAS +IconForm="frmUnixText" +Startup="frmUnixText" +HelpFile="" +Title="Unix Text Generator" +ExeName32="UNIXTEXT.EXE" +Path32="..\..\BIN" +Command32="" +Name="prjUnixText" +HelpContextID="0" +CompatibleMode="0" +MajorVer=1 +MinorVer=0 +RevisionVer=0 +AutoIncrementVer=0 +ServerSupportFiles=0 +VersionCompanyName="Unknown Organization" +CompilationType=0 +OptimizationType=0 +FavorPentiumPro(tm)=0 +CodeViewDebugInfo=0 +NoAliasing=0 +BoundsCheck=0 +OverflowCheck=0 +FlPointCheck=0 +FDIVCheck=0 +UnroundedFP=0 +StartMode=0 +Unattended=0 +Retained=0 +ThreadPerObject=0 +MaxNumberOfThreads=1 +DebugStartupOption=0 + +[MS Transaction Server] +AutoRefresh=1 diff --git a/codec/unixtext/dos2mac.c b/codec/unixtext/dos2mac.c new file mode 100644 index 0000000..fe9b7c7 --- /dev/null +++ b/codec/unixtext/dos2mac.c @@ -0,0 +1,43 @@ +#include + +const char *szTempFile = "DOS2MAC.TMP"; + +int main(int argc, char **argv) { + FILE *fpIn, *fpOut; + int nChar; + + if (argc < 2) { + fpIn = stdin; + fpOut = stdout; + } else { + fpIn = fopen(argv[1], "rb"); + if (fpIn == NULL) { + return 1; + } + if (argc < 3) { + fpOut = fopen(szTempFile, "wb"); + } else { + fpOut = fopen(argv[2], "wb"); + } + if (fpOut == NULL) { + fclose(fpIn); + return 1; + } + } + + while ((nChar = fgetc(fpIn)) != EOF) { + if (nChar != '\n') { + fputc(nChar, fpOut); + } + } + + if (argc > 1) { + fclose(fpIn); + fclose(fpOut); + if (argc < 3) { + remove(argv[1]); + rename(szTempFile, argv[1]); + } + } + return 0; +} diff --git a/codec/unixtext/dos2unix.c b/codec/unixtext/dos2unix.c new file mode 100644 index 0000000..8e80b9b --- /dev/null +++ b/codec/unixtext/dos2unix.c @@ -0,0 +1,43 @@ +#include + +const char *szTempFile = "DOS2UNIX.TMP"; + +int main(int argc, char **argv) { + FILE *fpIn, *fpOut; + int nChar; + + if (argc < 2) { + fpIn = stdin; + fpOut = stdout; + } else { + fpIn = fopen(argv[1], "rb"); + if (fpIn == NULL) { + return 1; + } + if (argc < 3) { + fpOut = fopen(szTempFile, "wb"); + } else { + fpOut = fopen(argv[2], "wb"); + } + if (fpOut == NULL) { + fclose(fpIn); + return 1; + } + } + + while ((nChar = fgetc(fpIn)) != EOF) { + if (nChar != '\r') { + fputc(nChar, fpOut); + } + } + + if (argc > 1) { + fclose(fpIn); + fclose(fpOut); + if (argc < 3) { + remove(argv[1]); + rename(szTempFile, argv[1]); + } + } + return 0; +} diff --git a/codec/unixtext/mac2dos.c b/codec/unixtext/mac2dos.c new file mode 100644 index 0000000..274659b --- /dev/null +++ b/codec/unixtext/mac2dos.c @@ -0,0 +1,44 @@ +#include + +const char *szTempFile = "MAC2DOS.TMP"; + +int main(int argc, char **argv) { + FILE *fpIn, *fpOut; + int nChar; + + if (argc < 2) { + fpIn = stdin; + fpOut = stdout; + } else { + fpIn = fopen(argv[1], "rb"); + if (fpIn == NULL) { + return 1; + } + if (argc < 3) { + fpOut = fopen(szTempFile, "wb"); + } else { + fpOut = fopen(argv[2], "wb"); + } + if (fpOut == NULL) { + fclose(fpIn); + return 1; + } + } + + while ((nChar = fgetc(fpIn)) != EOF) { + fputc(nChar, fpOut); + if (nChar == '\r') { + fputc('\n', fpOut); + } + } + + if (argc > 1) { + fclose(fpIn); + fclose(fpOut); + if (argc < 3) { + remove(argv[1]); + rename(szTempFile, argv[1]); + } + } + return 0; +} diff --git a/codec/unixtext/unix2dos.c b/codec/unixtext/unix2dos.c new file mode 100644 index 0000000..f3ce513 --- /dev/null +++ b/codec/unixtext/unix2dos.c @@ -0,0 +1,44 @@ +#include + +const char *szTempFile = "UNIX2DOS.TMP"; + +int main(int argc, char **argv) { + FILE *fpIn, *fpOut; + int nChar; + + if (argc < 2) { + fpIn = stdin; + fpOut = stdout; + } else { + fpIn = fopen(argv[1], "rb"); + if (fpIn == NULL) { + return 1; + } + if (argc < 3) { + fpOut = fopen(szTempFile, "wb"); + } else { + fpOut = fopen(argv[2], "wb"); + } + if (fpOut == NULL) { + fclose(fpIn); + return 1; + } + } + + while ((nChar = fgetc(fpIn)) != EOF) { + if (nChar == '\n') { + fputc('\r', fpOut); + } + fputc(nChar, fpOut); + } + + if (argc > 1) { + fclose(fpIn); + fclose(fpOut); + if (argc < 3) { + remove(argv[1]); + rename(szTempFile, argv[1]); + } + } + return 0; +} diff --git a/eleeye/MAKEFILE.BAT b/eleeye/MAKEFILE.BAT new file mode 100644 index 0000000..a6f1a11 --- /dev/null +++ b/eleeye/MAKEFILE.BAT @@ -0,0 +1,5 @@ +@ECHO OFF +RC ..\RES\ELEEYE.RC +CL /DNDEBUG /O2 /W3 /Fe..\BIN\ELEEYE.EXE ..\BASE\PIPE.CPP UCCI.CPP PREGEN.CPP POSITION.CPP GENMOVES.CPP HASH.CPP BOOK.CPP MOVESORT.CPP SEARCH.CPP ELEEYE.CPP SHLWAPI.LIB ..\RES\ELEEYE.RES +DEL ..\RES\ELEEYE.RES +DEL *.OBJ diff --git a/eleeye/book.cpp b/eleeye/book.cpp new file mode 100644 index 0000000..ed445f6 --- /dev/null +++ b/eleeye/book.cpp @@ -0,0 +1,105 @@ +/* +book.h/book.cpp - Source Code for ElephantEye, Part VI + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.1, Last Modified: Nov. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "position.h" +#include "book.h" + +int GetBookMoves(const PositionStruct &pos, const char *szBookFile, MoveStruct *lpmvs) { + BookFileStruct BookFile; + PositionStruct posScan; + BookStruct bk; + int nScan, nLow, nHigh, nPtr; + int i, j, nMoves; + // 从开局库中搜索着法的例程,有以下几个步骤: + + // 1. 打开开局库,如果打开失败,则返回空值; + if (!BookFile.Open(szBookFile)) { + return 0; + } + + // 2. 用拆半查找法搜索局面; + posScan = pos; + for (nScan = 0; nScan < 2; nScan ++) { + nPtr = nLow = 0; + nHigh = BookFile.nLen - 1; + while (nLow <= nHigh) { + nPtr = (nLow + nHigh) / 2; + BookFile.Read(bk, nPtr); + if (BOOK_POS_CMP(bk, posScan) < 0) { + nLow = nPtr + 1; + } else if (BOOK_POS_CMP(bk, posScan) > 0) { + nHigh = nPtr - 1; + } else { + break; + } + } + if (nLow <= nHigh) { + break; + } + // 原局面和镜像局面各搜索一趟 + posScan.Mirror(); + } + + // 3. 如果不到局面,则返回空着; + if (nScan == 2) { + BookFile.Close(); + return 0; + } + __ASSERT_BOUND(0, nPtr, BookFile.nLen - 1); + + // 4. 如果找到局面,则向前查找第一个着法; + for (nPtr --; nPtr >= 0; nPtr --) { + BookFile.Read(bk, nPtr); + if (BOOK_POS_CMP(bk, posScan) < 0) { + break; + } + } + + // 5. 向后依次读入属于该局面的每个着法; + nMoves = 0; + for (nPtr ++; nPtr < BookFile.nLen; nPtr ++) { + BookFile.Read(bk, nPtr); + if (BOOK_POS_CMP(bk, posScan) > 0) { + break; + } + if (posScan.LegalMove(bk.wmv)) { + // 如果局面是第二趟搜索到的,则着法必须做镜像 + lpmvs[nMoves].wmv = (nScan == 0 ? bk.wmv : MOVE_MIRROR(bk.wmv)); + lpmvs[nMoves].wvl = bk.wvl; + nMoves ++; + if (nMoves == MAX_GEN_MOVES) { + break; + } + } + } + BookFile.Close(); + + // 6. 对着法按分值排序 + for (i = 0; i < nMoves - 1; i ++) { + for (j = nMoves - 1; j > i; j --) { + if (lpmvs[j - 1].wvl < lpmvs[j].wvl) { + SWAP(lpmvs[j - 1], lpmvs[j]); + } + } + } + return nMoves; +} diff --git a/eleeye/book.h b/eleeye/book.h new file mode 100644 index 0000000..a3ae446 --- /dev/null +++ b/eleeye/book.h @@ -0,0 +1,65 @@ +/* +book.h/book.cpp - Source Code for ElephantEye, Part VI + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.1, Last Modified: Nov. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef BOOK_H +#define BOOK_H + +#include +#include "../base/base.h" +#include "position.h" + +struct BookStruct { + uint32_t dwZobristLock; + uint16_t wmv, wvl; +}; // bk + +inline int BOOK_POS_CMP(const BookStruct &bk, const PositionStruct &pos) { + return bk.dwZobristLock < pos.zobr.dwLock1 ? -1 : + bk.dwZobristLock > pos.zobr.dwLock1 ? 1 : 0; +} + +struct BookFileStruct { + FILE *fp; + int nLen; + bool Open(const char *szFileName) { + fp = fopen(szFileName, "rb"); + if (fp == NULL) { + return false; + } else { + fseek(fp, 0, SEEK_END); + nLen = ftell(fp) / sizeof(BookStruct); + return true; + } + } + void Close(void) const { + fclose(fp); + } + void Read(BookStruct &bk, int nPtr) const { + fseek(fp, nPtr * sizeof(BookStruct), SEEK_SET); + fread(&bk, sizeof(BookStruct), 1, fp); + } +}; + +// 获取开局库着法 +int GetBookMoves(const PositionStruct &pos, const char *szBookFile, MoveStruct *lpmvs); + +#endif diff --git a/eleeye/eleeye.cpp b/eleeye/eleeye.cpp new file mode 100644 index 0000000..732a1fe --- /dev/null +++ b/eleeye/eleeye.cpp @@ -0,0 +1,344 @@ +/* +eleeye.cpp - Source Code for ElephantEye, Part IX + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.15, Last Modified: Jul. 2008 +Copyright (C) 2004-2008 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "../base/base2.h" +#include "../base/parse.h" +#include "ucci.h" +#include "pregen.h" +#include "position.h" +#include "hash.h" +#include "search.h" + +#ifdef _WIN32 + #include + const char *const cszLibEvalFile = "EVALUATE.DLL"; +#else + #include + #define WINAPI + const char *const cszLibEvalFile = "libeval.so"; +#endif + +const int INTERRUPT_COUNT = 4096; // 搜索若干结点后调用中断 + +static const char *WINAPI GetEngineName(void) { + return NULL; +} + +static void WINAPI PreEvaluate(PositionStruct *lppos, PreEvalStruct *lpPreEval) { + // 缺省的局面预评价过程,什么都不做 +} + +static int WINAPI Evaluate(const PositionStruct *lppos, int vlAlpha, int vlBeta) { + // 缺省的局面评价过程,只返回子力价值 + return lppos->Material(); +} + +#ifdef _WIN32 + +inline HMODULE LoadEvalApi(const char *szLibEvalFile) { + HMODULE hModule; + hModule = LoadLibrary(szLibEvalFile); + if (hModule == NULL) { + Search.GetEngineName = GetEngineName; + Search.PreEvaluate = PreEvaluate; + Search.Evaluate = Evaluate; + } else { + Search.GetEngineName = (const char *(WINAPI *)(void)) GetProcAddress(hModule, "_GetEngineName@0"); + Search.PreEvaluate = (void (WINAPI *)(PositionStruct *, PreEvalStruct *)) GetProcAddress(hModule, "_PreEvaluate@8"); + Search.Evaluate = (int (WINAPI *)(const PositionStruct *, int, int)) GetProcAddress(hModule, "_Evaluate@12"); + } + return hModule; +} + +inline void FreeEvalApi(HMODULE hModule) { + if (hModule != NULL) { + FreeLibrary(hModule); + } +} + +#else + +inline void *LoadEvalApi(const char *szLibEvalFile) { + void *hModule; + hModule = dlopen(szLibEvalFile, RTLD_LAZY); + if (hModule == NULL) { + Search.GetEngineName = GetEngineName; + Search.PreEvaluate = PreEvaluate; + Search.Evaluate = Evaluate; + } else { + Search.GetEngineName = (const char *(*)(void)) dlsym(hModule, "GetEngineName"); + Search.PreEvaluate = (void (*)(PositionStruct *, PreEvalStruct *)) dlsym(hModule, "PreEvaluate"); + Search.Evaluate = (int (*)(const PositionStruct *, int, int)) dlsym(hModule, "Evaluate"); + } + return hModule; +} + +inline void FreeEvalApi(void *hModule) { + if (hModule != NULL) { + dlclose(hModule); + } +} + +#endif + +inline void PrintLn(const char *sz) { + printf("%s\n", sz); + fflush(stdout); +} + +int main(void) { + int i; + bool bPonderTime; + UcciCommStruct UcciComm; + char szLibEvalFile[1024]; + const char *szEngineName; + PositionStruct posProbe; +#ifdef _WIN32 + HMODULE hModule; +#else + void *hModule; +#endif + + if (BootLine() != UCCI_COMM_UCCI) { + return 0; + } + LocatePath(Search.szBookFile, "BOOK.DAT"); + LocatePath(szLibEvalFile, cszLibEvalFile); + hModule = LoadEvalApi(szLibEvalFile); + bPonderTime = false; + PreGenInit(); + NewHash(24); // 24=16MB, 25=32MB, 26=64MB, ... + Search.pos.FromFen("rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w - - 0 1"); + Search.pos.nDistance = 0; + Search.PreEvaluate(&Search.pos, &PreEval); + Search.nBanMoves = 0; + Search.bQuit = Search.bBatch = Search.bDebug = Search.bAlwaysCheck = false; + Search.bUseHash = Search.bUseBook = Search.bNullMove = Search.bKnowledge = true; + Search.bIdle = false; + Search.nCountMask = INTERRUPT_COUNT - 1; + Search.nRandomMask = 0; + Search.rc4Random.InitRand(); + szEngineName = Search.GetEngineName(); + if (szEngineName == NULL) { + PrintLn("id name ElephantEye"); + } else { + printf("id name %s / ElephantEye\n", szEngineName); + fflush(stdout); + } + PrintLn("id version 3.15"); + PrintLn("id copyright 2004-2008 www.elephantbase.net"); + PrintLn("id author Morning Yellow"); + PrintLn("id user ElephantEye Test Team"); + PrintLn("option usemillisec type check default true"); + PrintLn("option promotion type check default false"); + PrintLn("option batch type check default false"); + PrintLn("option debug type check default false"); + PrintLn("option ponder type check default false"); + PrintLn("option alwayscheck type check default false"); + PrintLn("option usehash type check default true"); + PrintLn("option usebook type check default true"); + printf("option bookfiles type string default %s\n", Search.szBookFile); + fflush(stdout); + printf("option evalapi type string default %s\n", szLibEvalFile); + fflush(stdout); + PrintLn("option hashsize type spin min 16 max 1024 default 16"); + PrintLn("option idle type combo var none var small var medium var large default none"); + PrintLn("option pruning type combo var none var small var medium var large default large"); + PrintLn("option knowledge type combo var none var small var medium var large default large"); + PrintLn("option randomness type combo var none var small var medium var large default none"); + PrintLn("option newgame type button"); + PrintLn("ucciok"); + + // 以下是接收指令和提供对策的循环体 + while (!Search.bQuit) { + switch (IdleLine(UcciComm, Search.bDebug)) { + case UCCI_COMM_ISREADY: + PrintLn("readyok"); + break; + case UCCI_COMM_STOP: + PrintLn("nobestmove"); + break; + case UCCI_COMM_POSITION: + BuildPos(Search.pos, UcciComm); + Search.pos.nDistance = 0; + Search.PreEvaluate(&Search.pos, &PreEval); + Search.nBanMoves = 0; + break; + case UCCI_COMM_BANMOVES: + Search.nBanMoves = UcciComm.nBanMoveNum; + for (i = 0; i < UcciComm.nBanMoveNum; i ++) { + Search.wmvBanList[i] = COORD_MOVE(UcciComm.lpdwBanMovesCoord[i]); + } + break; + case UCCI_COMM_SETOPTION: + switch (UcciComm.Option) { + case UCCI_OPTION_PROMOTION: + PreEval.bPromotion = UcciComm.bCheck; + break; + case UCCI_OPTION_BATCH: + Search.bBatch = UcciComm.bCheck; + break; + case UCCI_OPTION_DEBUG: + Search.bDebug = UcciComm.bCheck; + break; + case UCCI_OPTION_PONDER: + bPonderTime = UcciComm.bCheck; + break; + case UCCI_OPTION_ALWAYSCHECK: + Search.bAlwaysCheck = UcciComm.bCheck; + break; + case UCCI_OPTION_USEHASH: + Search.bUseHash = UcciComm.bCheck; + break; + case UCCI_OPTION_USEBOOK: + Search.bUseBook = UcciComm.bCheck; + break; + case UCCI_OPTION_BOOKFILES: + if (AbsolutePath(UcciComm.szOption)) { + strcpy(Search.szBookFile, UcciComm.szOption); + } else { + LocatePath(Search.szBookFile, UcciComm.szOption); + } + break; + case UCCI_OPTION_EVALAPI: + if (AbsolutePath(UcciComm.szOption)) { + strcpy(szLibEvalFile, UcciComm.szOption); + } else { + LocatePath(szLibEvalFile, UcciComm.szOption); + } + FreeEvalApi(hModule); + hModule = LoadEvalApi(szLibEvalFile); + break; + case UCCI_OPTION_HASHSIZE: + DelHash(); + i = 19; // 小于1,分配0.5M置换表 + while (UcciComm.nSpin > 0) { + UcciComm.nSpin /= 2; + i ++; + } + NewHash(MAX(i, 24)); // 最小的置换表设为16M + break; + case UCCI_OPTION_IDLE: + switch (UcciComm.Grade) { + case UCCI_GRADE_NONE: + Search.bIdle = false; + Search.nCountMask = INTERRUPT_COUNT - 1; + break; + case UCCI_GRADE_SMALL: + Search.bIdle = true; + Search.nCountMask = INTERRUPT_COUNT / 4 - 1; + break; + case UCCI_GRADE_MEDIUM: + Search.bIdle = true; + Search.nCountMask = INTERRUPT_COUNT / 16 - 1; + break; + case UCCI_GRADE_LARGE: + Search.bIdle = true; + Search.nCountMask = INTERRUPT_COUNT / 64 - 1; + break; + default: + break; + } + break; + case UCCI_OPTION_PRUNING: + Search.bNullMove = (UcciComm.Grade != UCCI_GRADE_NONE); + break; + case UCCI_OPTION_KNOWLEDGE: + Search.bKnowledge = (UcciComm.Grade != UCCI_GRADE_NONE); + break; + case UCCI_OPTION_RANDOMNESS: + switch (UcciComm.Grade) { + case UCCI_GRADE_NONE: + Search.nRandomMask = 0; + break; + case UCCI_GRADE_SMALL: + Search.nRandomMask = 1; + break; + case UCCI_GRADE_MEDIUM: + Search.nRandomMask = 3; + break; + case UCCI_GRADE_LARGE: + Search.nRandomMask = 7; + break; + default: + break; + } + break; + default: + break; + } + break; + case UCCI_COMM_GO: + Search.bPonder = UcciComm.bPonder; + Search.bDraw = UcciComm.bDraw; + switch (UcciComm.Go) { + case UCCI_GO_DEPTH: + Search.nGoMode = GO_MODE_INFINITY; + Search.nNodes = 0; + SearchMain(UcciComm.nDepth); + break; + case UCCI_GO_NODES: + Search.nGoMode = GO_MODE_NODES; + Search.nNodes = UcciComm.nNodes; + SearchMain(UCCI_MAX_DEPTH); + break; + case UCCI_GO_TIME_MOVESTOGO: + case UCCI_GO_TIME_INCREMENT: + Search.nGoMode = GO_MODE_TIMER; + if (UcciComm.Go == UCCI_GO_TIME_MOVESTOGO) { + // 对于时段制,把剩余时间平均分配到每一步,作为适当时限。 + // 剩余步数从1到5,最大时限依次是剩余时间的100%、90%、80%、70%和60%,5以上都是50% + Search.nProperTimer = UcciComm.nTime / UcciComm.nMovesToGo; + Search.nMaxTimer = UcciComm.nTime * MAX(5, 11 - UcciComm.nMovesToGo) / 10; + } else { + // 对于加时制,假设棋局会在20回合内结束,算出平均每一步的适当时限,最大时限是剩余时间的一半 + Search.nProperTimer = UcciComm.nTime / 20 + UcciComm.nIncrement; + Search.nMaxTimer = UcciComm.nTime / 2; + } + // 如果是后台思考的时间分配策略,那么适当时限设为原来的1.25倍 + Search.nProperTimer += (bPonderTime ? Search.nProperTimer / 4 : 0); + Search.nMaxTimer = MIN(Search.nMaxTimer, Search.nProperTimer * 10); + SearchMain(UCCI_MAX_DEPTH); + break; + default: + break; + } + break; + case UCCI_COMM_PROBE: + BuildPos(posProbe, UcciComm); + if (!PopHash(posProbe)) { + PopLeaf(posProbe); + } + break; + case UCCI_COMM_QUIT: + Search.bQuit = true; + break; + default: + break; + } + } + DelHash(); + FreeEvalApi(hModule); + PrintLn("bye"); + return 0; +} diff --git a/eleeye/evaluate/MAKEFILE.BAT b/eleeye/evaluate/MAKEFILE.BAT new file mode 100644 index 0000000..a456081 --- /dev/null +++ b/eleeye/evaluate/MAKEFILE.BAT @@ -0,0 +1,7 @@ +@ECHO OFF +RC ..\..\RES\EVALUATE.RC +CL /DNDEBUG /O2 /W3 /LD /Fe..\..\BIN\EVALUATE.DLL ..\PREGEN.CPP ..\POSITION.CPP ..\GENMOVES.CPP PREEVAL.CPP EVALUATE.CPP ..\..\RES\EVALUATE.RES +DEL ..\..\RES\EVALUATE.RES +DEL *.OBJ +DEL ..\..\BIN\*.LIB +DEL ..\..\BIN\*.EXP \ No newline at end of file diff --git a/eleeye/evaluate/evaluate.cpp b/eleeye/evaluate/evaluate.cpp new file mode 100644 index 0000000..1a40fb2 --- /dev/null +++ b/eleeye/evaluate/evaluate.cpp @@ -0,0 +1,632 @@ +/* +evaluate.cpp - Source Code for ElephantEye, Part XI + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 2.34, Last Modified: Oct. 2006 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../../base/base.h" +#include "../pregen.h" +#include "../position.h" +#include "preeval.h" + +#if _WIN32 + +#include + +extern "C" __declspec(dllexport) int WINAPI Evaluate(const PositionStruct *lppos, int vlAlpha, int vlBeta); +extern "C" __declspec(dllexport) const char *WINAPI GetEngineName(void); + +#else + +#define WINAPI + +extern "C" int Evaluate(const PositionStruct *lppos, int vlAlpha, int vlBeta); +extern "C" const char *GetEngineName(void); + +#endif + +/* ElephantEye源程序使用的匈牙利记号约定: + * + * sq: 格子序号(整数,从0到255,参阅"pregen.cpp") + * pc: 棋子序号(整数,从0到47,参阅"position.cpp") + * pt: 棋子类型序号(整数,从0到6,参阅"position.cpp") + * mv: 着法(整数,从0到65535,参阅"position.cpp") + * sd: 走子方(整数,0代表红方,1代表黑方) + * vl: 局面价值(整数,从"-MATE_VALUE"到"MATE_VALUE",参阅"position.cpp") + * (注:以上五个记号可与uc、dw等代表整数的记号配合使用) + * pos: 局面(PositionStruct类型,参阅"position.h") + * sms: 位行和位列的着法生成预置结构(参阅"pregen.h") + * smv: 位行和位列的着法判断预置结构(参阅"pregen.h") + */ + +// 偷懒评价的边界 +const int EVAL_MARGIN1 = 160; +const int EVAL_MARGIN2 = 80; +const int EVAL_MARGIN3 = 40; +const int EVAL_MARGIN4 = 20; + +// 本模块只涉及到"PositionStruct"中的"sdPlayer"、"ucpcSquares"、"ucsqPieces"和"wBitPiece"四个成员,故省略前面的"this->" + +/* ElephantEye的局面评价内容共4有4部分 + * 1. 跟仕(士)有关的特殊棋型的评价,见"AdvisorShape()"; + * 2. 车或炮牵制帅(将)或车的棋型的评价,见"StringHold()"; + * 3. 车的灵活性的评价,见"RookMobility()"; + * 4. 马受到阻碍的评价,见"KnightTrap()"。 + */ + +// 以下是第一部分,特殊棋型的评价 + +/* 仕(士)的形状对于局面评价,特别是判断空头炮、沉底炮等棋型有重大作用,为此ElephantEye给出四种形状: + * 1. 帅(将)在原位,双仕(士)都在底线,编为1号,这种情况要判断空头炮和炮镇窝心马; + * 2. 帅(将)在原位,双仕(士)从左边包围帅(将),编为2号,这种情况要判断右边的沉底炮和车封右边的帅(将)门; + * 3. 帅(将)在原位,双仕(士)从右边包围帅(将),编为3号,这种情况要判断左边的沉底炮和车封左边的帅(将)门; + * 4. 其他情况,包括帅(将)不在原位或缺仕(士),都编号0。 + * 注:以“红下黑上”这个固定的棋盘方位来规定左右。 + */ +const int WHITE_KING_BITFILE = 1 << (RANK_BOTTOM - RANK_TOP); +const int BLACK_KING_BITFILE = 1 << (RANK_TOP - RANK_TOP); +const int KING_BITRANK = 1 << (FILE_CENTER - FILE_LEFT); + +const int SHAPE_NONE = 0; +const int SHAPE_CENTER = 1; +const int SHAPE_LEFT = 2; +const int SHAPE_RIGHT = 3; + +int AdvisorShape(const PositionStruct *lppos) { + int pcCannon, pcRook, sq, sqAdv1, sqAdv2, x, y, nShape; + int vlWhitePenalty, vlBlackPenalty; + SlideMaskStruct *lpsms; + vlWhitePenalty = vlBlackPenalty = 0; + if ((lppos->wBitPiece[0] & ADVISOR_BITPIECE) == ADVISOR_BITPIECE) { + if (lppos->ucsqPieces[SIDE_TAG(0) + KING_FROM] == 0xc7) { + sqAdv1 = lppos->ucsqPieces[SIDE_TAG(0) + ADVISOR_FROM]; + sqAdv2 = lppos->ucsqPieces[SIDE_TAG(0) + ADVISOR_TO]; + if (false) { + } else if (sqAdv1 == 0xc6) { // 红方一个仕在左侧底线 + nShape = (sqAdv2 == 0xc8 ? SHAPE_CENTER : sqAdv2 == 0xb7 ? SHAPE_LEFT : SHAPE_NONE); + } else if (sqAdv1 == 0xc8) { // 红方一个仕在右侧底线 + nShape = (sqAdv2 == 0xc6 ? SHAPE_CENTER : sqAdv2 == 0xb7 ? SHAPE_RIGHT : SHAPE_NONE); + } else if (sqAdv1 == 0xb7) { // 红方一个仕在花心 + nShape = (sqAdv2 == 0xc6 ? SHAPE_LEFT : sqAdv2 == 0xc8 ? SHAPE_RIGHT : SHAPE_NONE); + } else { + nShape = SHAPE_NONE; + } + switch (nShape) { + case SHAPE_NONE: + break; + case SHAPE_CENTER: + for (pcCannon = SIDE_TAG(1) + CANNON_FROM; pcCannon <= SIDE_TAG(1) + CANNON_TO; pcCannon ++) { + sq = lppos->ucsqPieces[pcCannon]; + if (sq != 0) { + x = FILE_X(sq); + if (x == FILE_CENTER) { + y = RANK_Y(sq); + lpsms = lppos->FileMaskPtr(x, y); + if ((lpsms->wRookCap & WHITE_KING_BITFILE) != 0) { + // 计算空头炮的威胁 + vlWhitePenalty += PreEvalEx.vlHollowThreat[RANK_FLIP(y)]; + } else if ((lpsms->wSuperCap & WHITE_KING_BITFILE) != 0 && + (lppos->ucpcSquares[0xb7] == 21 || lppos->ucpcSquares[0xb7] == 22)) { + // 计算炮镇窝心马的威胁 + vlWhitePenalty += PreEvalEx.vlCentralThreat[RANK_FLIP(y)]; + } + } + } + } + break; + case SHAPE_LEFT: + case SHAPE_RIGHT: + for (pcCannon = SIDE_TAG(1) + CANNON_FROM; pcCannon <= SIDE_TAG(1) + CANNON_TO; pcCannon ++) { + sq = lppos->ucsqPieces[pcCannon]; + if (sq != 0) { + x = FILE_X(sq); + y = RANK_Y(sq); + if (x == FILE_CENTER) { + if ((lppos->FileMaskPtr(x, y)->wSuperCap & WHITE_KING_BITFILE) != 0) { + // 计算一般中炮的威胁,帅(将)门被对方控制的还有额外罚分 + vlWhitePenalty += (PreEvalEx.vlCentralThreat[RANK_FLIP(y)] >> 2) + + (lppos->Protected(1, nShape == SHAPE_LEFT ? 0xc8 : 0xc6) ? 20 : 0); + // 如果车在底线保护帅(将),则给予更大的罚分! + for (pcRook = SIDE_TAG(0) + ROOK_FROM; pcRook <= SIDE_TAG(0) + ROOK_TO; pcRook ++) { + sq = lppos->ucsqPieces[pcRook]; + if (sq != 0) { + y = RANK_Y(sq); + if (y == RANK_BOTTOM) { + x = FILE_X(sq); + if ((lppos->RankMaskPtr(x, y)->wRookCap & KING_BITRANK) != 0) { + vlWhitePenalty += 80; + } + } + } + } + } + } else if (y == RANK_BOTTOM) { + if ((lppos->RankMaskPtr(x, y)->wRookCap & KING_BITRANK) != 0) { + // 计算沉底炮的威胁 + vlWhitePenalty += PreEvalEx.vlWhiteBottomThreat[x]; + } + } + } + } + break; + default: + break; + } + } else if (lppos->ucsqPieces[SIDE_TAG(0) + KING_FROM] == 0xb7) { + // 有双仕(士)但花心被帅(将)占领,要罚分 + vlWhitePenalty += 20; + } + } else { + if ((lppos->wBitPiece[1] & ROOK_BITPIECE) == ROOK_BITPIECE) { + // 缺仕(士)怕双车,有罚分 + vlWhitePenalty += PreEvalEx.vlWhiteAdvisorLeakage; + } + } + if ((lppos->wBitPiece[1] & ADVISOR_BITPIECE) == ADVISOR_BITPIECE) { + if (lppos->ucsqPieces[SIDE_TAG(1) + KING_FROM] == 0x37) { + sqAdv1 = lppos->ucsqPieces[SIDE_TAG(1) + ADVISOR_FROM]; + sqAdv2 = lppos->ucsqPieces[SIDE_TAG(1) + ADVISOR_TO]; + if (false) { + } else if (sqAdv1 == 0x36) { // 黑方一个士在左侧底线 + nShape = (sqAdv2 == 0x38 ? SHAPE_CENTER : sqAdv2 == 0x47 ? SHAPE_LEFT : SHAPE_NONE); + } else if (sqAdv1 == 0x38) { // 黑方一个士在右侧底线 + nShape = (sqAdv2 == 0x36 ? SHAPE_CENTER : sqAdv2 == 0x47 ? SHAPE_RIGHT : SHAPE_NONE); + } else if (sqAdv1 == 0x47) { // 黑方一个士在花心 + nShape = (sqAdv2 == 0x36 ? SHAPE_LEFT : sqAdv2 == 0x38 ? SHAPE_RIGHT : SHAPE_NONE); + } else { + nShape = SHAPE_NONE; + } + switch (nShape) { + case SHAPE_NONE: + break; + case SHAPE_CENTER: + for (pcCannon = SIDE_TAG(0) + CANNON_FROM; pcCannon <= SIDE_TAG(0) + CANNON_TO; pcCannon ++) { + sq = lppos->ucsqPieces[pcCannon]; + if (sq != 0) { + x = FILE_X(sq); + if (x == FILE_CENTER) { + y = RANK_Y(sq); + lpsms = lppos->FileMaskPtr(x, y); + if ((lpsms->wRookCap & BLACK_KING_BITFILE) != 0) { + // 计算空头炮的威胁 + vlBlackPenalty += PreEvalEx.vlHollowThreat[y]; + } else if ((lpsms->wSuperCap & BLACK_KING_BITFILE) != 0 && + (lppos->ucpcSquares[0x47] == 37 || lppos->ucpcSquares[0x47] == 38)) { + // 计算炮镇窝心马的威胁 + vlBlackPenalty += PreEvalEx.vlCentralThreat[y]; + } + } + } + } + break; + case SHAPE_LEFT: + case SHAPE_RIGHT: + for (pcCannon = SIDE_TAG(0) + CANNON_FROM; pcCannon <= SIDE_TAG(0) + CANNON_TO; pcCannon ++) { + sq = lppos->ucsqPieces[pcCannon]; + if (sq != 0) { + x = FILE_X(sq); + y = RANK_Y(sq); + if (x == FILE_CENTER) { + if ((lppos->FileMaskPtr(x, y)->wSuperCap & BLACK_KING_BITFILE) != 0) { + // 计算一般中炮的威胁,帅(将)门被对方控制的还有额外罚分 + vlBlackPenalty += (PreEvalEx.vlCentralThreat[y] >> 2) + + (lppos->Protected(0, nShape == SHAPE_LEFT ? 0x38 : 0x36) ? 20 : 0); + // 如果车在底线保护帅(将),则给予更大的罚分! + for (pcRook = SIDE_TAG(1) + ROOK_FROM; pcRook <= SIDE_TAG(1) + ROOK_TO; pcRook ++) { + sq = lppos->ucsqPieces[pcRook]; + if (sq != 0) { + y = RANK_Y(sq); + if (y == RANK_TOP) { + x = FILE_X(sq); + if ((lppos->RankMaskPtr(x, y)->wRookCap & KING_BITRANK) != 0) { + vlBlackPenalty += 80; + } + } + } + } + } + } else if (y == RANK_TOP) { + if ((lppos->RankMaskPtr(x, y)->wRookCap & KING_BITRANK) != 0) { + // 计算沉底炮的威胁 + vlBlackPenalty += PreEvalEx.vlBlackBottomThreat[x]; + } + } + } + } + break; + default: + break; + } + } else if (lppos->ucsqPieces[SIDE_TAG(1) + KING_FROM] == 0x47) { + // 有双仕(士)但花心被帅(将)占领,要罚分 + vlBlackPenalty += 20; + } + } else { + if ((lppos->wBitPiece[0] & ROOK_BITPIECE) == ROOK_BITPIECE) { + // 缺仕(士)怕双车,有罚分 + vlBlackPenalty += PreEvalEx.vlBlackAdvisorLeakage; + } + } + return SIDE_VALUE(lppos->sdPlayer, vlBlackPenalty - vlWhitePenalty); +} + +// 以上是第一部分,特殊棋型的评价 + +// 以下是第二部分,牵制的评价 + +// 常数表"cnValuableStringPieces"用判断牵制是否有价值 +// 大于0的项是对于车来说的,牵制马和炮(被牵制)都有价值,大于1的项是对于炮来说只有牵制马才有价值 +static const int cnValuableStringPieces[48] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 0, 0, 0 +}; + +// "ccvlStringValueTab"是类似"KNIGHT_PIN_TAB"的常数表(参阅"pregen.h"),决定牵制价值 +// 中间子和被牵制子的距离越近,牵制的价值就越大 +static const char ccvlStringValueTab[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, + 12, 16, 20, 24, 28, 32, 36, 0, 36, 32, 28, 24, 20, 16, 12, 0, + 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 +}; + +// 车或炮牵制帅(将)或车的棋型的评价 +int StringHold(const PositionStruct *lppos) { + int sd, i, j, nDir, sqSrc, sqDst, sqStr; + int x, y, nSideTag, nOppSideTag; + int vlString[2]; + SlideMoveStruct *lpsmv; + + for (sd = 0; sd < 2; sd ++) { + vlString[sd] = 0; + nSideTag = SIDE_TAG(sd); + nOppSideTag = OPP_SIDE_TAG(sd); + // 考查用车来牵制的情况 + for (i = ROOK_FROM; i <= ROOK_TO; i ++) { + sqSrc = lppos->ucsqPieces[nSideTag + i]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + // 考查牵制目标是帅(将)的情况 + sqDst = lppos->ucsqPieces[nOppSideTag + KING_FROM]; + if (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + x = FILE_X(sqSrc); + y = RANK_Y(sqSrc); + if (x == FILE_X(sqDst)) { + lpsmv = lppos->FileMovePtr(x, y); + nDir = (sqSrc < sqDst ? 0 : 1); + // 如果车用炮的吃法(炮用超级炮的着法)能吃到目标子"sqDst",牵制就成立了,下同 + if (sqDst == lpsmv->ucCannonCap[nDir] + FILE_DISP(x)) { + // 被牵制子"sqStr"是车(炮)本身能吃到的棋子,下同 + sqStr = lpsmv->ucRookCap[nDir] + FILE_DISP(x); + __ASSERT_SQUARE(sqStr); + // 被牵制子必须是对方的子,下同 + if ((lppos->ucpcSquares[sqStr] & nOppSideTag) != 0) { + // 如果被牵制子是有价值的,而且被牵制子没有保护(被目标子保护不算),那么牵制是有价值的,下同 + if (cnValuableStringPieces[lppos->ucpcSquares[sqStr]] > 0 && + !lppos->Protected(OPP_SIDE(sd), sqStr, sqDst)) { + vlString[sd] += ccvlStringValueTab[sqDst - sqStr + 256]; + } + } + } + } else if (y == RANK_Y(sqDst)) { + lpsmv = lppos->RankMovePtr(x, y); + nDir = (sqSrc < sqDst ? 0 : 1); + if (sqDst == lpsmv->ucCannonCap[nDir] + RANK_DISP(y)) { + sqStr = lpsmv->ucRookCap[nDir] + RANK_DISP(y); + __ASSERT_SQUARE(sqStr); + if ((lppos->ucpcSquares[sqStr] & nOppSideTag) != 0) { + if (cnValuableStringPieces[lppos->ucpcSquares[sqStr]] > 0 && + !lppos->Protected(OPP_SIDE(sd), sqStr, sqDst)) { + vlString[sd] += ccvlStringValueTab[sqDst - sqStr + 256]; + } + } + } + } + } + // 考查牵制目标是车的情况 + for (j = ROOK_FROM; j <= ROOK_TO; j ++) { + sqDst = lppos->ucsqPieces[nOppSideTag + j]; + if (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + x = FILE_X(sqSrc); + y = RANK_Y(sqSrc); + if (x == FILE_X(sqDst)) { + lpsmv = lppos->FileMovePtr(x, y); + nDir = (sqSrc < sqDst ? 0 : 1); + if (sqDst == lpsmv->ucCannonCap[nDir] + FILE_DISP(x)) { + sqStr = lpsmv->ucRookCap[nDir] + FILE_DISP(x); + __ASSERT_SQUARE(sqStr); + if ((lppos->ucpcSquares[sqStr] & nOppSideTag) != 0) { + // 目标子是车,不同于帅(将),要求车也没有保护时才有牵制价值,下同 + if (cnValuableStringPieces[lppos->ucpcSquares[sqStr]] > 0 && + !lppos->Protected(OPP_SIDE(sd), sqDst) && !lppos->Protected(OPP_SIDE(sd), sqStr, sqDst)) { + vlString[sd] += ccvlStringValueTab[sqDst - sqStr + 256]; + } + } + } + } else if (y == RANK_Y(sqDst)) { + lpsmv = lppos->RankMovePtr(x, y); + nDir = (sqSrc < sqDst ? 0 : 1); + if (sqDst == lpsmv->ucCannonCap[nDir] + RANK_DISP(y)) { + sqStr = lpsmv->ucRookCap[nDir] + RANK_DISP(y); + __ASSERT_SQUARE(sqStr); + if ((lppos->ucpcSquares[sqStr] & nOppSideTag) != 0) { + if (cnValuableStringPieces[lppos->ucpcSquares[sqStr]] > 0 && + !lppos->Protected(OPP_SIDE(sd), sqDst) && !lppos->Protected(OPP_SIDE(sd), sqStr, sqDst)) { + vlString[sd] += ccvlStringValueTab[sqDst - sqStr + 256]; + } + } + } + } + } + } + } + } + + // 考查用炮来牵制的情况 + for (i = CANNON_FROM; i <= CANNON_TO; i ++) { + sqSrc = lppos->ucsqPieces[nSideTag + i]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + // 考查牵制目标是帅(将)的情况 + sqDst = lppos->ucsqPieces[nOppSideTag + KING_FROM]; + if (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + x = FILE_X(sqSrc); + y = RANK_Y(sqSrc); + if (x == FILE_X(sqDst)) { + lpsmv = lppos->FileMovePtr(x, y); + nDir = (sqSrc < sqDst ? 0 : 1); + if (sqDst == lpsmv->ucSuperCap[nDir] + FILE_DISP(x)) { + sqStr = lpsmv->ucCannonCap[nDir] + FILE_DISP(x); + __ASSERT_SQUARE(sqStr); + if ((lppos->ucpcSquares[sqStr] & nOppSideTag) != 0) { + if (cnValuableStringPieces[lppos->ucpcSquares[sqStr]] > 1 && + !lppos->Protected(OPP_SIDE(sd), sqStr, sqDst)) { + vlString[sd] += ccvlStringValueTab[sqDst - sqStr + 256]; + } + } + } + } else if (y == RANK_Y(sqDst)) { + lpsmv = lppos->RankMovePtr(x, y); + nDir = (sqSrc < sqDst ? 0 : 1); + if (sqDst == lpsmv->ucSuperCap[nDir] + RANK_DISP(y)) { + sqStr = lpsmv->ucCannonCap[nDir] + RANK_DISP(y); + __ASSERT_SQUARE(sqStr); + if ((lppos->ucpcSquares[sqStr] & nOppSideTag) != 0) { + if (cnValuableStringPieces[lppos->ucpcSquares[sqStr]] > 1 && + !lppos->Protected(OPP_SIDE(sd), sqStr, sqDst)) { + vlString[sd] += ccvlStringValueTab[sqDst - sqStr + 256]; + } + } + } + } + } + // 考查牵制目标是车的情况 + for (j = ROOK_FROM; j <= ROOK_TO; j ++) { + sqDst = lppos->ucsqPieces[nOppSideTag + j]; + if (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + x = FILE_X(sqSrc); + y = RANK_Y(sqSrc); + if (x == FILE_X(sqDst)) { + lpsmv = lppos->FileMovePtr(x, y); + nDir = (sqSrc < sqDst ? 0 : 1); + if (sqDst == lpsmv->ucSuperCap[nDir] + FILE_DISP(x)) { + sqStr = lpsmv->ucCannonCap[nDir] + FILE_DISP(x); + __ASSERT_SQUARE(sqStr); + if ((lppos->ucpcSquares[sqStr] & nOppSideTag) != 0) { + if (cnValuableStringPieces[lppos->ucpcSquares[sqStr]] > 1 && + !lppos->Protected(OPP_SIDE(sd), sqStr, sqDst)) { + vlString[sd] += ccvlStringValueTab[sqDst - sqStr + 256]; + } + } + } + } else if (y == RANK_Y(sqDst)) { + lpsmv = lppos->RankMovePtr(x, y); + nDir = (sqSrc < sqDst ? 0 : 1); + if (sqDst == lpsmv->ucSuperCap[nDir] + RANK_DISP(y)) { + sqStr = lpsmv->ucCannonCap[nDir] + RANK_DISP(y); + __ASSERT_SQUARE(sqStr); + if ((lppos->ucpcSquares[sqStr] & nOppSideTag) != 0) { + if (cnValuableStringPieces[lppos->ucpcSquares[sqStr]] > 1 && + !lppos->Protected(OPP_SIDE(sd), sqStr, sqDst)) { + vlString[sd] += ccvlStringValueTab[sqDst - sqStr + 256]; + } + } + } + } + } + } + } + } + } + return SIDE_VALUE(lppos->sdPlayer, vlString[0] - vlString[1]); +} + +// 以上是第二部分,牵制的评价 + +// 以下是第三部分,车的灵活性的评价 + +int RookMobility(const PositionStruct *lppos) { + int sd, i, sqSrc, nSideTag, x, y; + int vlRookMobility[2]; + for (sd = 0; sd < 2; sd ++) { + vlRookMobility[sd] = 0; + nSideTag = SIDE_TAG(sd); + for (i = ROOK_FROM; i <= ROOK_TO; i ++) { + sqSrc = lppos->ucsqPieces[nSideTag + i]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + x = FILE_X(sqSrc); + y = RANK_Y(sqSrc); + vlRookMobility[sd] += PreEvalEx.cPopCnt16[lppos->RankMaskPtr(x, y)->wNonCap] + + PreEvalEx.cPopCnt16[lppos->FileMaskPtr(x, y)->wNonCap]; + } + } + __ASSERT(vlRookMobility[sd] <= 34); + } + return SIDE_VALUE(lppos->sdPlayer, vlRookMobility[0] - vlRookMobility[1]) >> 1; +} + +// 以上是第三部分,车的灵活性的评价 + +// 以下是第四部分,马受到阻碍的评价 + +// 常数表"cbcEdgeSquares"给定了不利于马的位置,处于棋盘边缘和两个花心位置的马都是坏马 +static const bool cbcEdgeSquares[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +int KnightTrap(const PositionStruct *lppos) { + int sd, i, sqSrc, sqDst, nSideTag, nMovable; + uint8_t *lpucsqDst, *lpucsqPin; + int vlKnightTraps[2]; + + for (sd = 0; sd < 2; sd ++) { + vlKnightTraps[sd] = 0; + nSideTag = SIDE_TAG(sd); + // 考虑马可以走的位置,走到棋盘边缘上,或者走到对方的控制格,都必须排除 + for (i = KNIGHT_FROM; i <= KNIGHT_TO; i ++) { + sqSrc = lppos->ucsqPieces[nSideTag + i]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + nMovable = 0; + lpucsqDst = PreGen.ucsqKnightMoves[sqSrc]; + lpucsqPin = PreGen.ucsqKnightPins[sqSrc]; + sqDst = *lpucsqDst; + while (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + // 以下的判断区别于"genmoves.cpp"中的着法生成器,排除了走到棋盘边缘和走到对方控制格的着法 + if (!cbcEdgeSquares[sqDst] && lppos->ucpcSquares[sqDst] == 0 && + lppos->ucpcSquares[*lpucsqPin] == 0 && !lppos->Protected(OPP_SIDE(sd), sqDst)) { + nMovable ++; + if (nMovable > 1) { + break; + } + } + lpucsqDst ++; + sqDst = *lpucsqDst; + lpucsqPin ++; + } + // 没有好的着法的马给予10分罚分,只有一个好的着法的马给予5分罚分 + if (nMovable == 0) { + vlKnightTraps[sd] += 10; + } else if (nMovable == 1) { + vlKnightTraps[sd] += 5; + } + } + __ASSERT(vlKnightTraps[sd] <= 20); + } + } + return SIDE_VALUE(lppos->sdPlayer, vlKnightTraps[1] - vlKnightTraps[0]); +} + +// 以上是第四部分,马受到阻碍的评价 + +// 局面评价过程 +int WINAPI Evaluate(const PositionStruct *lppos, int vlAlpha, int vlBeta) { + int vl; + // 偷懒的局面评价函数分以下几个层次: + + // 1. 四级偷懒评价(彻底偷懒评价),只包括子力平衡; + vl = lppos->Material(); + if (vl + EVAL_MARGIN1 <= vlAlpha) { + return vl + EVAL_MARGIN1; + } else if (vl - EVAL_MARGIN1 >= vlBeta) { + return vl - EVAL_MARGIN1; + } + + // 2. 三级偷懒评价,包括特殊棋型; + vl += AdvisorShape(lppos); + if (vl + EVAL_MARGIN2 <= vlAlpha) { + return vl + EVAL_MARGIN2; + } else if (vl - EVAL_MARGIN2 >= vlBeta) { + return vl - EVAL_MARGIN2; + } + + // 3. 二级偷懒评价,包括牵制; + vl += StringHold(lppos); + if (vl + EVAL_MARGIN3 <= vlAlpha) { + return vl + EVAL_MARGIN3; + } else if (vl - EVAL_MARGIN3 >= vlBeta) { + return vl - EVAL_MARGIN3; + } + + // 4. 一级偷懒评价,包括车的灵活性; + vl += RookMobility(lppos); + if (vl + EVAL_MARGIN4 <= vlAlpha) { + return vl + EVAL_MARGIN4; + } else if (vl - EVAL_MARGIN4 >= vlBeta) { + return vl - EVAL_MARGIN4; + } + + // 5. 零级偷懒评价(完全评价),包括马的阻碍。 + return vl + KnightTrap(lppos); +} + +const char *WINAPI GetEngineName(void) { + return NULL; +} diff --git a/eleeye/evaluate/makefile.sh b/eleeye/evaluate/makefile.sh new file mode 100644 index 0000000..578e593 --- /dev/null +++ b/eleeye/evaluate/makefile.sh @@ -0,0 +1 @@ +g++ -DNDEBUG -O4 -Wall -shared -olibeval.so -fPIC ../pregen.cpp ../position.cpp ../genmoves.cpp preeval.cpp evaluate.cpp \ No newline at end of file diff --git a/eleeye/evaluate/preeval.cpp b/eleeye/evaluate/preeval.cpp new file mode 100644 index 0000000..b0d8ed6 --- /dev/null +++ b/eleeye/evaluate/preeval.cpp @@ -0,0 +1,500 @@ +/* +preeval.h/preeval.cpp - Source Code for ElephantEye, Part X + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.0, Last Modified: Nov. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../../base/base.h" +#include "../pregen.h" +#include "../position.h" +#include "preeval.h" + +#if _WIN32 + +#include +extern "C" __declspec(dllexport) void WINAPI PreEvaluate(PositionStruct *lppos, PreEvalStruct *lpPreEval); + +#else + +#define WINAPI +extern "C" void WINAPI PreEvaluate(PositionStruct *lppos, PreEvalStruct *lpPreEval); + +#endif + +/* ElephantEye源程序使用的匈牙利记号约定: + * + * sq: 格子序号(整数,从0到255,参阅"pregen.cpp") + * pc: 棋子序号(整数,从0到47,参阅"position.cpp") + * pt: 棋子类型序号(整数,从0到6,参阅"position.cpp") + * mv: 着法(整数,从0到65535,参阅"position.cpp") + * sd: 走子方(整数,0代表红方,1代表黑方) + * vl: 局面价值(整数,从"-MATE_VALUE"到"MATE_VALUE",参阅"position.cpp") + * (注:以上五个记号可与uc、dw等代表整数的记号配合使用) + * pos: 局面(PositionStruct类型,参阅"position.h") + * sms: 位行和位列的着法生成预置结构(参阅"pregen.h") + * smv: 位行和位列的着法判断预置结构(参阅"pregen.h") + */ + +/* 子力位置价值表 + * ElephantEye的子力位置价值表对局面评价的导向起了很大的作用,在参照“梦入神蛋”程序的基础上,作了如下改动: + * 1. 把棋力基本分和位置相关分组合在一起,以利于快速运算; + * 2. 一九路的兵(卒)在巡河位置分值减少了5分,以减少盲目进边兵(卒)的情况; + * 3. 过河兵(卒)(底线除外)多加10分,以减少过河兵(卒)盲目换仕(士)相(象)的情况; + * 4. 一九路车在横车的位置分值减少了5分,以减少上仕(士)时还起无意义的横车的情况。 + */ + +// 1. 开中局、有进攻机会的帅(将)和兵(卒),参照“梦入神蛋” +static const uint8_t cucvlKingPawnMidgameAttacking[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 9, 9, 11, 13, 11, 9, 9, 9, 0, 0, 0, 0, + 0, 0, 0, 39, 49, 69, 84, 89, 84, 69, 49, 39, 0, 0, 0, 0, + 0, 0, 0, 39, 49, 64, 74, 74, 74, 64, 49, 39, 0, 0, 0, 0, + 0, 0, 0, 39, 46, 54, 59, 61, 59, 54, 46, 39, 0, 0, 0, 0, + 0, 0, 0, 29, 37, 41, 54, 59, 54, 41, 37, 29, 0, 0, 0, 0, + 0, 0, 0, 7, 0, 13, 0, 16, 0, 13, 0, 7, 0, 0, 0, 0, + 0, 0, 0, 7, 0, 7, 0, 15, 0, 7, 0, 7, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 11, 15, 11, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// 2. 开中局、没有进攻机会的帅(将)和兵(卒) +static const uint8_t cucvlKingPawnMidgameAttackless[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 9, 9, 11, 13, 11, 9, 9, 9, 0, 0, 0, 0, + 0, 0, 0, 19, 24, 34, 42, 44, 42, 34, 24, 19, 0, 0, 0, 0, + 0, 0, 0, 19, 24, 32, 37, 37, 37, 32, 24, 19, 0, 0, 0, 0, + 0, 0, 0, 19, 23, 27, 29, 30, 29, 27, 23, 19, 0, 0, 0, 0, + 0, 0, 0, 14, 18, 20, 27, 29, 27, 20, 18, 14, 0, 0, 0, 0, + 0, 0, 0, 7, 0, 13, 0, 16, 0, 13, 0, 7, 0, 0, 0, 0, + 0, 0, 0, 7, 0, 7, 0, 15, 0, 7, 0, 7, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 11, 15, 11, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// 3. 残局、有进攻机会的帅(将)和兵(卒) +static const uint8_t cucvlKingPawnEndgameAttacking[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 10, 10, 15, 15, 15, 10, 10, 10, 0, 0, 0, 0, + 0, 0, 0, 50, 55, 60, 85,100, 85, 60, 55, 50, 0, 0, 0, 0, + 0, 0, 0, 65, 70, 70, 75, 75, 75, 70, 70, 65, 0, 0, 0, 0, + 0, 0, 0, 75, 80, 80, 80, 80, 80, 80, 80, 75, 0, 0, 0, 0, + 0, 0, 0, 70, 70, 65, 70, 70, 70, 65, 70, 70, 0, 0, 0, 0, + 0, 0, 0, 45, 0, 40, 45, 45, 45, 40, 0, 45, 0, 0, 0, 0, + 0, 0, 0, 40, 0, 35, 40, 40, 40, 35, 0, 40, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 5, 15, 5, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3, 3, 13, 3, 3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 11, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// 4. 残局、没有进攻机会的帅(将)和兵(卒) +static const uint8_t cucvlKingPawnEndgameAttackless[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 10, 10, 15, 15, 15, 10, 10, 10, 0, 0, 0, 0, + 0, 0, 0, 10, 15, 20, 45, 60, 45, 20, 15, 10, 0, 0, 0, 0, + 0, 0, 0, 25, 30, 30, 35, 35, 35, 30, 30, 25, 0, 0, 0, 0, + 0, 0, 0, 35, 40, 40, 45, 45, 45, 40, 40, 35, 0, 0, 0, 0, + 0, 0, 0, 25, 30, 30, 35, 35, 35, 30, 30, 25, 0, 0, 0, 0, + 0, 0, 0, 25, 0, 25, 25, 25, 25, 25, 0, 25, 0, 0, 0, 0, + 0, 0, 0, 20, 0, 20, 20, 20, 20, 20, 0, 20, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 5, 13, 5, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3, 3, 12, 3, 3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 11, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// 5. 没受威胁的仕(士)和相(象) +static const uint8_t cucvlAdvisorBishopThreatless[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 20, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 18, 0, 0, 20, 23, 20, 0, 0, 18, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 20, 20, 0, 20, 20, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// 5'. 可升变的,没受威胁的仕(士)和相(象) +static const uint8_t cucvlAdvisorBishopPromotionThreatless[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 30, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 28, 0, 0, 30, 33, 30, 0, 0, 28, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 30, 30, 0, 30, 30, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// 6. 受到威胁的仕(士)和相(象),参照“梦入神蛋” +static const uint8_t cucvlAdvisorBishopThreatened[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 40, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 38, 0, 0, 40, 43, 40, 0, 0, 38, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 43, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 40, 40, 0, 40, 40, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// 7. 开中局的马,参照“梦入神蛋” +static const uint8_t cucvlKnightMidgame[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 90, 90, 90, 96, 90, 96, 90, 90, 90, 0, 0, 0, 0, + 0, 0, 0, 90, 96,103, 97, 94, 97,103, 96, 90, 0, 0, 0, 0, + 0, 0, 0, 92, 98, 99,103, 99,103, 99, 98, 92, 0, 0, 0, 0, + 0, 0, 0, 93,108,100,107,100,107,100,108, 93, 0, 0, 0, 0, + 0, 0, 0, 90,100, 99,103,104,103, 99,100, 90, 0, 0, 0, 0, + 0, 0, 0, 90, 98,101,102,103,102,101, 98, 90, 0, 0, 0, 0, + 0, 0, 0, 92, 94, 98, 95, 98, 95, 98, 94, 92, 0, 0, 0, 0, + 0, 0, 0, 93, 92, 94, 95, 92, 95, 94, 92, 93, 0, 0, 0, 0, + 0, 0, 0, 85, 90, 92, 93, 78, 93, 92, 90, 85, 0, 0, 0, 0, + 0, 0, 0, 88, 85, 90, 88, 90, 88, 90, 85, 88, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// 8. 残局的马 +static const uint8_t cucvlKnightEndgame[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 92, 94, 96, 96, 96, 96, 96, 94, 92, 0, 0, 0, 0, + 0, 0, 0, 94, 96, 98, 98, 98, 98, 98, 96, 94, 0, 0, 0, 0, + 0, 0, 0, 96, 98,100,100,100,100,100, 98, 96, 0, 0, 0, 0, + 0, 0, 0, 96, 98,100,100,100,100,100, 98, 96, 0, 0, 0, 0, + 0, 0, 0, 96, 98,100,100,100,100,100, 98, 96, 0, 0, 0, 0, + 0, 0, 0, 94, 96, 98, 98, 98, 98, 98, 96, 94, 0, 0, 0, 0, + 0, 0, 0, 94, 96, 98, 98, 98, 98, 98, 96, 94, 0, 0, 0, 0, + 0, 0, 0, 92, 94, 96, 96, 96, 96, 96, 94, 92, 0, 0, 0, 0, + 0, 0, 0, 90, 92, 94, 92, 92, 92, 94, 92, 90, 0, 0, 0, 0, + 0, 0, 0, 88, 90, 92, 90, 90, 90, 92, 90, 88, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// 9. 开中局的车,参照“梦入神蛋” +static const uint8_t cucvlRookMidgame[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,206,208,207,213,214,213,207,208,206, 0, 0, 0, 0, + 0, 0, 0,206,212,209,216,233,216,209,212,206, 0, 0, 0, 0, + 0, 0, 0,206,208,207,214,216,214,207,208,206, 0, 0, 0, 0, + 0, 0, 0,206,213,213,216,216,216,213,213,206, 0, 0, 0, 0, + 0, 0, 0,208,211,211,214,215,214,211,211,208, 0, 0, 0, 0, + 0, 0, 0,208,212,212,214,215,214,212,212,208, 0, 0, 0, 0, + 0, 0, 0,204,209,204,212,214,212,204,209,204, 0, 0, 0, 0, + 0, 0, 0,198,208,204,212,212,212,204,208,198, 0, 0, 0, 0, + 0, 0, 0,200,208,206,212,200,212,206,208,200, 0, 0, 0, 0, + 0, 0, 0,194,206,204,212,200,212,204,206,194, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// 10. 残局的车 +static const uint8_t cucvlRookEndgame[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,182,182,182,184,186,184,182,182,182, 0, 0, 0, 0, + 0, 0, 0,184,184,184,186,190,186,184,184,184, 0, 0, 0, 0, + 0, 0, 0,182,182,182,184,186,184,182,182,182, 0, 0, 0, 0, + 0, 0, 0,180,180,180,182,184,182,180,180,180, 0, 0, 0, 0, + 0, 0, 0,180,180,180,182,184,182,180,180,180, 0, 0, 0, 0, + 0, 0, 0,180,180,180,182,184,182,180,180,180, 0, 0, 0, 0, + 0, 0, 0,180,180,180,182,184,182,180,180,180, 0, 0, 0, 0, + 0, 0, 0,180,180,180,182,184,182,180,180,180, 0, 0, 0, 0, + 0, 0, 0,180,180,180,182,184,182,180,180,180, 0, 0, 0, 0, + 0, 0, 0,180,180,180,182,184,182,180,180,180, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// 11. 开中局的炮,参照“梦入神蛋” +static const uint8_t cucvlCannonMidgame[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,100,100, 96, 91, 90, 91, 96,100,100, 0, 0, 0, 0, + 0, 0, 0, 98, 98, 96, 92, 89, 92, 96, 98, 98, 0, 0, 0, 0, + 0, 0, 0, 97, 97, 96, 91, 92, 91, 96, 97, 97, 0, 0, 0, 0, + 0, 0, 0, 96, 99, 99, 98,100, 98, 99, 99, 96, 0, 0, 0, 0, + 0, 0, 0, 96, 96, 96, 96,100, 96, 96, 96, 96, 0, 0, 0, 0, + 0, 0, 0, 95, 96, 99, 96,100, 96, 99, 96, 95, 0, 0, 0, 0, + 0, 0, 0, 96, 96, 96, 96, 96, 96, 96, 96, 96, 0, 0, 0, 0, + 0, 0, 0, 97, 96,100, 99,101, 99,100, 96, 97, 0, 0, 0, 0, + 0, 0, 0, 96, 97, 98, 98, 98, 98, 98, 97, 96, 0, 0, 0, 0, + 0, 0, 0, 96, 96, 97, 99, 99, 99, 97, 96, 96, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// 12. 残局的炮 +static const uint8_t cucvlCannonEndgame[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,100,100,100,100,100,100,100,100,100, 0, 0, 0, 0, + 0, 0, 0,100,100,100,100,100,100,100,100,100, 0, 0, 0, 0, + 0, 0, 0,100,100,100,100,100,100,100,100,100, 0, 0, 0, 0, + 0, 0, 0,100,100,100,102,104,102,100,100,100, 0, 0, 0, 0, + 0, 0, 0,100,100,100,102,104,102,100,100,100, 0, 0, 0, 0, + 0, 0, 0,100,100,100,102,104,102,100,100,100, 0, 0, 0, 0, + 0, 0, 0,100,100,100,102,104,102,100,100,100, 0, 0, 0, 0, + 0, 0, 0,100,100,100,102,104,102,100,100,100, 0, 0, 0, 0, + 0, 0, 0,100,100,100,104,106,104,100,100,100, 0, 0, 0, 0, + 0, 0, 0,100,100,100,104,106,104,100,100,100, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// 空头炮的威胁分值,指标是对红方来说的行号(即黑方要用15去减),大体上空头炮位置越高威胁越大。进入残局时,该值要相应减少。 +static const int cvlHollowThreat[16] = { + 0, 0, 0, 0, 0, 0, 60, 65, 70, 75, 80, 80, 80, 0, 0, 0 +}; + +// 炮镇窝心马的威胁分值,指标同上,大体上高度越低威胁越大,没有窝心马时可取四分之一。进入残局时,取值似乎不应变化。 +static const int cvlCentralThreat[16] = { + 0, 0, 0, 0, 0, 0, 50, 45, 40, 35, 30, 30, 30, 0, 0, 0 +}; + +// 沉底炮的威胁分值,指标是列号,大体上越靠近边线威胁越大。威胁减少时,该值要相应减少。 +static const int cvlBottomThreat[16] = { + 0, 0, 0, 40, 30, 0, 0, 0, 0, 0, 30, 40, 0, 0, 0, 0 +}; + +// 本模块只涉及到"PositionStruct"中的"ucsqPieces"、"dwBitPiece/wBitPiece"、"vlWhite"和"vlBlack"四个成员,故省略前面的"this->" + +/* 局面预评价就是初始化局面预评价数据(PreEval和PreEvalEx)的过程。 + * ElephantEye的局面预评价主要分以下两个方面: + * 1. 判断局势处于开中局还是残局阶段; + * 2. 判断每一方是否对对方形成威胁。 + */ + +const int ROOK_MIDGAME_VALUE = 6; +const int KNIGHT_CANNON_MIDGAME_VALUE = 3; +const int OTHER_MIDGAME_VALUE = 1; +const int TOTAL_MIDGAME_VALUE = ROOK_MIDGAME_VALUE * 4 + KNIGHT_CANNON_MIDGAME_VALUE * 8 + OTHER_MIDGAME_VALUE * 18; +const int TOTAL_ADVANCED_VALUE = 4; +const int TOTAL_ATTACK_VALUE = 8; +const int ADVISOR_BISHOP_ATTACKLESS_VALUE = 80; +const int TOTAL_ADVISOR_LEAKAGE = 80; + +static bool bInit = false; + +PreEvalStructEx PreEvalEx; + +void WINAPI PreEvaluate(PositionStruct *lppos, PreEvalStruct *lpPreEval) { + int i, sq, nMidgameValue, nWhiteAttacks, nBlackAttacks, nWhiteSimpleValue, nBlackSimpleValue; + uint8_t ucvlPawnPiecesAttacking[256], ucvlPawnPiecesAttackless[256]; + + if (!bInit) { + bInit = true; + // 初始化"PreEvalEx.cPopCnt16"数组,只需要初始化一次 + for (i = 0; i < 65536; i ++) { + PreEvalEx.cPopCnt16[i] = PopCnt16(i); + } + // 注意:引擎和局面评价API函数拥有两个不同的PreGen实例! + PreGenInit(); + } + PreEval.bPromotion = lpPreEval->bPromotion; + + // 首先判断局势处于开中局还是残局阶段,方法是计算各种棋子的数量,按照车=6、马炮=3、其它=1相加。 + nMidgameValue = PopCnt32(lppos->dwBitPiece & BOTH_BITPIECE(ADVISOR_BITPIECE | BISHOP_BITPIECE | PAWN_BITPIECE)) * OTHER_MIDGAME_VALUE; + nMidgameValue += PopCnt32(lppos->dwBitPiece & BOTH_BITPIECE(KNIGHT_BITPIECE | CANNON_BITPIECE)) * KNIGHT_CANNON_MIDGAME_VALUE; + nMidgameValue += PopCnt32(lppos->dwBitPiece & BOTH_BITPIECE(ROOK_BITPIECE)) * ROOK_MIDGAME_VALUE; + // 使用二次函数,子力很少时才认为接近残局 + nMidgameValue = (2 * TOTAL_MIDGAME_VALUE - nMidgameValue) * nMidgameValue / TOTAL_MIDGAME_VALUE; + __ASSERT_BOUND(0, nMidgameValue, TOTAL_MIDGAME_VALUE); + PreEval.vlAdvanced = (TOTAL_ADVANCED_VALUE * nMidgameValue + TOTAL_ADVANCED_VALUE / 2) / TOTAL_MIDGAME_VALUE; + __ASSERT_BOUND(0, PreEval.vlAdvanced, TOTAL_ADVANCED_VALUE); + for (sq = 0; sq < 256; sq ++) { + if (IN_BOARD(sq)) { + PreEval.ucvlWhitePieces[0][sq] = PreEval.ucvlBlackPieces[0][SQUARE_FLIP(sq)] = (uint8_t) + ((cucvlKingPawnMidgameAttacking[sq] * nMidgameValue + cucvlKingPawnEndgameAttacking[sq] * (TOTAL_MIDGAME_VALUE - nMidgameValue)) / TOTAL_MIDGAME_VALUE); + PreEval.ucvlWhitePieces[3][sq] = PreEval.ucvlBlackPieces[3][SQUARE_FLIP(sq)] = (uint8_t) + ((cucvlKnightMidgame[sq] * nMidgameValue + cucvlKnightEndgame[sq] * (TOTAL_MIDGAME_VALUE - nMidgameValue)) / TOTAL_MIDGAME_VALUE); + PreEval.ucvlWhitePieces[4][sq] = PreEval.ucvlBlackPieces[4][SQUARE_FLIP(sq)] = (uint8_t) + ((cucvlRookMidgame[sq] * nMidgameValue + cucvlRookEndgame[sq] * (TOTAL_MIDGAME_VALUE - nMidgameValue)) / TOTAL_MIDGAME_VALUE); + PreEval.ucvlWhitePieces[5][sq] = PreEval.ucvlBlackPieces[5][SQUARE_FLIP(sq)] = (uint8_t) + ((cucvlCannonMidgame[sq] * nMidgameValue + cucvlCannonEndgame[sq] * (TOTAL_MIDGAME_VALUE - nMidgameValue)) / TOTAL_MIDGAME_VALUE); + ucvlPawnPiecesAttacking[sq] = PreEval.ucvlWhitePieces[0][sq]; + ucvlPawnPiecesAttackless[sq] = (uint8_t) + ((cucvlKingPawnMidgameAttackless[sq] * nMidgameValue + cucvlKingPawnEndgameAttackless[sq] * (TOTAL_MIDGAME_VALUE - nMidgameValue)) / TOTAL_MIDGAME_VALUE); + } + } + for (i = 0; i < 16; i ++) { + PreEvalEx.vlHollowThreat[i] = cvlHollowThreat[i] * (nMidgameValue + TOTAL_MIDGAME_VALUE) / (TOTAL_MIDGAME_VALUE * 2); + __ASSERT_BOUND(0, PreEvalEx.vlHollowThreat[i], cvlHollowThreat[i]); + PreEvalEx.vlCentralThreat[i] = cvlCentralThreat[i]; + } + + // 然后判断各方是否处于进攻状态,方法是计算各种过河棋子的数量,按照车马2炮兵1相加。 + nWhiteAttacks = nBlackAttacks = 0; + for (i = SIDE_TAG(0) + KNIGHT_FROM; i <= SIDE_TAG(0) + ROOK_TO; i ++) { + if (lppos->ucsqPieces[i] != 0 && BLACK_HALF(lppos->ucsqPieces[i])) { + nWhiteAttacks += 2; + } + } + for (i = SIDE_TAG(0) + CANNON_FROM; i <= SIDE_TAG(0) + PAWN_TO; i ++) { + if (lppos->ucsqPieces[i] != 0 && BLACK_HALF(lppos->ucsqPieces[i])) { + nWhiteAttacks ++; + } + } + for (i = SIDE_TAG(1) + KNIGHT_FROM; i <= SIDE_TAG(1) + ROOK_TO; i ++) { + if (lppos->ucsqPieces[i] != 0 && WHITE_HALF(lppos->ucsqPieces[i])) { + nBlackAttacks += 2; + } + } + for (i = SIDE_TAG(1) + CANNON_FROM; i <= SIDE_TAG(1) + PAWN_TO; i ++) { + if (lppos->ucsqPieces[i] != 0 && WHITE_HALF(lppos->ucsqPieces[i])) { + nBlackAttacks ++; + } + } + // 如果本方轻子数比对方多,那么每多一个轻子(车算2个轻子)威胁值加2。威胁值最多不超过8。 + nWhiteSimpleValue = PopCnt16(lppos->wBitPiece[0] & ROOK_BITPIECE) * 2 + PopCnt16(lppos->wBitPiece[0] & (KNIGHT_BITPIECE | CANNON_BITPIECE)); + nBlackSimpleValue = PopCnt16(lppos->wBitPiece[1] & ROOK_BITPIECE) * 2 + PopCnt16(lppos->wBitPiece[1] & (KNIGHT_BITPIECE | CANNON_BITPIECE)); + if (nWhiteSimpleValue > nBlackSimpleValue) { + nWhiteAttacks += (nWhiteSimpleValue - nBlackSimpleValue) * 2; + } else { + nBlackAttacks += (nBlackSimpleValue - nWhiteSimpleValue) * 2; + } + nWhiteAttacks = MIN(nWhiteAttacks, TOTAL_ATTACK_VALUE); + nBlackAttacks = MIN(nBlackAttacks, TOTAL_ATTACK_VALUE); + PreEvalEx.vlBlackAdvisorLeakage = TOTAL_ADVISOR_LEAKAGE * nWhiteAttacks / TOTAL_ATTACK_VALUE; + PreEvalEx.vlWhiteAdvisorLeakage = TOTAL_ADVISOR_LEAKAGE * nBlackAttacks / TOTAL_ATTACK_VALUE; + __ASSERT_BOUND(0, nWhiteAttacks, TOTAL_ATTACK_VALUE); + __ASSERT_BOUND(0, nBlackAttacks, TOTAL_ATTACK_VALUE); + __ASSERT_BOUND(0, PreEvalEx.vlBlackAdvisorLeakage, TOTAL_ADVISOR_LEAKAGE); + __ASSERT_BOUND(0, PreEvalEx.vlBlackAdvisorLeakage, TOTAL_ADVISOR_LEAKAGE); + for (sq = 0; sq < 256; sq ++) { + if (IN_BOARD(sq)) { + PreEval.ucvlWhitePieces[1][sq] = PreEval.ucvlWhitePieces[2][sq] = (uint8_t) ((cucvlAdvisorBishopThreatened[sq] * nBlackAttacks + + (PreEval.bPromotion ? cucvlAdvisorBishopPromotionThreatless[sq] : cucvlAdvisorBishopThreatless[sq]) * (TOTAL_ATTACK_VALUE - nBlackAttacks)) / TOTAL_ATTACK_VALUE); + PreEval.ucvlBlackPieces[1][sq] = PreEval.ucvlBlackPieces[2][sq] = (uint8_t) ((cucvlAdvisorBishopThreatened[SQUARE_FLIP(sq)] * nWhiteAttacks + + (PreEval.bPromotion ? cucvlAdvisorBishopPromotionThreatless[SQUARE_FLIP(sq)] : cucvlAdvisorBishopThreatless[SQUARE_FLIP(sq)]) * (TOTAL_ATTACK_VALUE - nWhiteAttacks)) / TOTAL_ATTACK_VALUE); + PreEval.ucvlWhitePieces[6][sq] = (uint8_t) ((ucvlPawnPiecesAttacking[sq] * nWhiteAttacks + + ucvlPawnPiecesAttackless[sq] * (TOTAL_ATTACK_VALUE - nWhiteAttacks)) / TOTAL_ATTACK_VALUE); + PreEval.ucvlBlackPieces[6][sq] = (uint8_t) ((ucvlPawnPiecesAttacking[SQUARE_FLIP(sq)] * nBlackAttacks + + ucvlPawnPiecesAttackless[SQUARE_FLIP(sq)] * (TOTAL_ATTACK_VALUE - nBlackAttacks)) / TOTAL_ATTACK_VALUE); + } + } + for (i = 0; i < 16; i ++) { + PreEvalEx.vlWhiteBottomThreat[i] = cvlBottomThreat[i] * nBlackAttacks / TOTAL_ATTACK_VALUE; + PreEvalEx.vlBlackBottomThreat[i] = cvlBottomThreat[i] * nWhiteAttacks / TOTAL_ATTACK_VALUE; + } + + // 检查预评价是否对称 +#ifndef NDEBUG + for (sq = 0; sq < 256; sq ++) { + if (IN_BOARD(sq)) { + for (i = 0; i < 7; i ++) { + __ASSERT(PreEval.ucvlWhitePieces[i][sq] == PreEval.ucvlWhitePieces[i][SQUARE_MIRROR(sq)]); + __ASSERT(PreEval.ucvlBlackPieces[i][sq] == PreEval.ucvlBlackPieces[i][SQUARE_MIRROR(sq)]); + } + } + } + for (i = FILE_LEFT; i <= FILE_RIGHT; i ++) { + __ASSERT(PreEvalEx.vlWhiteBottomThreat[i] == PreEvalEx.vlWhiteBottomThreat[FILE_FLIP(i)]); + __ASSERT(PreEvalEx.vlBlackBottomThreat[i] == PreEvalEx.vlBlackBottomThreat[FILE_FLIP(i)]); + } +#endif + + // 调整不受威胁方少掉的仕(士)相(象)分值 + lppos->vlWhite = ADVISOR_BISHOP_ATTACKLESS_VALUE * (TOTAL_ATTACK_VALUE - nBlackAttacks) / TOTAL_ATTACK_VALUE; + lppos->vlBlack = ADVISOR_BISHOP_ATTACKLESS_VALUE * (TOTAL_ATTACK_VALUE - nWhiteAttacks) / TOTAL_ATTACK_VALUE; + // 如果允许升变,那么不受威胁的仕(士)相(象)分值就少了一半 + if (PreEval.bPromotion) { + lppos->vlWhite /= 2; + lppos->vlBlack /= 2; + } + // 最后重新计算子力位置分 + for (i = 16; i < 32; i ++) { + sq = lppos->ucsqPieces[i]; + if (sq != 0) { + __ASSERT_SQUARE(sq); + lppos->vlWhite += PreEval.ucvlWhitePieces[PIECE_TYPE(i)][sq]; + } + } + for (i = 32; i < 48; i ++) { + sq = lppos->ucsqPieces[i]; + if (sq != 0) { + __ASSERT_SQUARE(sq); + lppos->vlBlack += PreEval.ucvlBlackPieces[PIECE_TYPE(i)][sq]; + } + } + + // 注意:引擎和局面评价API函数拥有两个不同的PreEval实例! + *lpPreEval = PreEval; +} diff --git a/eleeye/evaluate/preeval.h b/eleeye/evaluate/preeval.h new file mode 100644 index 0000000..91a353b --- /dev/null +++ b/eleeye/evaluate/preeval.h @@ -0,0 +1,34 @@ +/* +preeval.h/preeval.cpp - Source Code for ElephantEye, Part X + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 2.1, Last Modified: May 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PREEVAL_H +#define PREEVAL_H + +// 扩展的局面预评价结构 +extern struct PreEvalStructEx { + int vlBlackAdvisorLeakage, vlWhiteAdvisorLeakage; + int vlHollowThreat[16], vlCentralThreat[16]; + int vlWhiteBottomThreat[16], vlBlackBottomThreat[16]; + char cPopCnt16[65536]; // 加速PopCnt16的数组,只需要初始化一次 +} PreEvalEx; + +#endif diff --git a/eleeye/genmoves.cpp b/eleeye/genmoves.cpp new file mode 100644 index 0000000..59fcbbe --- /dev/null +++ b/eleeye/genmoves.cpp @@ -0,0 +1,841 @@ +/* +genmoves.cpp - Source Code for ElephantEye, Part IV + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.0, Last Modified: Nov. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../base/base.h" +#include "pregen.h" +#include "position.h" + +/* ElephantEye源程序使用的匈牙利记号约定: + * + * sq: 格子序号(整数,从0到255,参阅"pregen.cpp") + * pc: 棋子序号(整数,从0到47,参阅"position.cpp") + * pt: 棋子类型序号(整数,从0到6,参阅"position.cpp") + * mv: 着法(整数,从0到65535,参阅"position.cpp") + * sd: 走子方(整数,0代表红方,1代表黑方) + * vl: 局面价值(整数,从"-MATE_VALUE"到"MATE_VALUE",参阅"position.cpp") + * (注:以上四个记号可与uc、dw等代表整数的记号配合使用) + * pos: 局面(PositionStruct类型,参阅"position.h") + * sms: 位行和位列的着法生成预置结构(参阅"pregen.h") + * smv: 位行和位列的着法判断预置结构(参阅"pregen.h") + */ + +// 本模块只涉及到"PositionStruct"中的"sdPlayer"、"ucpcSquares"和"ucsqPieces"三个成员,故省略前面的"this->" + +// 棋子保护判断 +bool PositionStruct::Protected(int sd, int sqSrc, int sqExcept) const { + // 参数"sqExcept"表示排除保护的棋子(指格子编号),考虑被牵制子的保护时,需要排除牵制目标子的保护 + int i, sqDst, sqPin, pc, x, y, nSideTag; + SlideMaskStruct *lpsmsRank, *lpsmsFile; + // 棋子保护判断包括以下几个步骤: + + __ASSERT_SQUARE(sqSrc); + nSideTag = SIDE_TAG(sd); + if (HOME_HALF(sqSrc, sd)) { + if (IN_FORT(sqSrc)) { + + // 1. 判断受到帅(将)的保护 + sqDst = ucsqPieces[nSideTag + KING_FROM]; + if (sqDst != 0 && sqDst != sqExcept) { + __ASSERT_SQUARE(sqDst); + if (KING_SPAN(sqSrc, sqDst)) { + return true; + } + } + + // 2. 判断受到仕(士)的保护 + for (i = ADVISOR_FROM; i <= ADVISOR_TO; i ++) { + sqDst = ucsqPieces[nSideTag + i]; + if (sqDst != 0 && sqDst != sqExcept) { + __ASSERT_SQUARE(sqDst); + if (ADVISOR_SPAN(sqSrc, sqDst)) { + return true; + } + } + } + } + + // 3. 判断受到相(象)的保护 + for (i = BISHOP_FROM; i <= BISHOP_TO; i ++) { + sqDst = ucsqPieces[nSideTag + i]; + if (sqDst != 0 && sqDst != sqExcept) { + __ASSERT_SQUARE(sqDst); + if (BISHOP_SPAN(sqSrc, sqDst) && ucpcSquares[BISHOP_PIN(sqSrc, sqDst)] == 0) { + return true; + } + } + } + } else { + + // 4. 判断受到过河兵(卒)横向的保护 + for (sqDst = sqSrc - 1; sqDst <= sqSrc + 1; sqDst += 2) { + // 如果棋子在边线,那么断言不成立 + // __ASSERT_SQUARE(sqDst); + if (sqDst != sqExcept) { + pc = ucpcSquares[sqDst]; + if ((pc & nSideTag) != 0 && PIECE_INDEX(pc) >= PAWN_FROM) { + return true; + } + } + } + } + + // 5. 判断受到兵(卒)纵向的保护 + sqDst = SQUARE_BACKWARD(sqSrc, sd); + // 如果棋子在底线,那么断言不成立 + // __ASSERT_SQUARE(sqDst); + if (sqDst != sqExcept) { + pc = ucpcSquares[sqDst]; + if ((pc & nSideTag) != 0 && PIECE_INDEX(pc) >= PAWN_FROM) { + return true; + } + } + + // 6. 判断受到马的保护 + for (i = KNIGHT_FROM; i <= KNIGHT_TO; i ++) { + sqDst = ucsqPieces[nSideTag + i]; + if (sqDst != 0 && sqDst != sqExcept) { + __ASSERT_SQUARE(sqDst); + sqPin = KNIGHT_PIN(sqDst, sqSrc); // 注意,sqSrc和sqDst是反的! + if (sqPin != sqDst && ucpcSquares[sqPin] == 0) { + return true; + } + } + } + + x = FILE_X(sqSrc); + y = RANK_Y(sqSrc); + lpsmsRank = RankMaskPtr(x, y); + lpsmsFile = FileMaskPtr(x, y); + + // 7. 判断受到车的保护,参阅"position.cpp"里的"CheckedBy()"函数 + for (i = ROOK_FROM; i <= ROOK_TO; i ++) { + sqDst = ucsqPieces[nSideTag + i]; + if (sqDst != 0 && sqDst != sqSrc && sqDst != sqExcept) { + if (x == FILE_X(sqDst)) { + if ((lpsmsFile->wRookCap & PreGen.wBitFileMask[sqDst]) != 0) { + return true; + } + } else if (y == RANK_Y(sqDst)) { + if ((lpsmsRank->wRookCap & PreGen.wBitRankMask[sqDst]) != 0) { + return true; + } + } + } + } + + // 8. 判断受到炮的保护,参阅"position.cpp"里的"CheckedBy()"函数 + for (i = CANNON_FROM; i <= CANNON_TO; i ++) { + sqDst = ucsqPieces[nSideTag + i]; + if (sqDst && sqDst != sqSrc && sqDst != sqExcept) { + if (x == FILE_X(sqDst)) { + if ((lpsmsFile->wCannonCap & PreGen.wBitFileMask[sqDst]) != 0) { + return true; + } + } else if (y == RANK_Y(sqDst)) { + if ((lpsmsRank->wCannonCap & PreGen.wBitRankMask[sqDst]) != 0) { + return true; + } + } + } + } + return false; +} + +/* 计算MVV(LVA)值的函数 + * + * MVV(LVA)指的是:如果被吃子无保护,那么取值MVV,否则取值MVV-LVA。 + * 由于ElephantEye的MVV(LVA)值在计算完毕后再加了1,并且有其它考虑,因此有以下几种含义: + * a. MVV(LVA)大于1,说明被吃子价值大于攻击子(表面上是赚的),这种吃子将首先搜索,静态搜索也将考虑这种吃子; + * b. MVV(LVA)等于1,说明被吃子有一定价值(吃车马炮或吃过河兵卒,即便表面上是亏的,也值得一试),静态搜索也将考虑这种吃子; + * c. MVV(LVA)等于0,说明被吃子没有价值,静态搜索将不考虑这种吃子。 + * + * MVV价值表"SIMPLE_VALUE"是按照帅(将)=5、车=4、马炮=3、兵(卒)=2、仕(士)相(象)=1设定的; + * LVA价值直接体现在吃子着法生成器中。 + */ +int PositionStruct::MvvLva(int sqDst, int pcCaptured, int nLva) const { + int nMvv, nLvaAdjust; + nMvv = SIMPLE_VALUE(pcCaptured); + nLvaAdjust = (Protected(OPP_SIDE(sdPlayer), sqDst) ? nLva : 0); + if (nMvv >= nLvaAdjust) { + return nMvv - nLvaAdjust + 1; + } else { + return (nMvv >= 3 || HOME_HALF(sqDst, sdPlayer)) ? 1 : 0; + } +} + +// 吃子着法生成器,按MVV(LVA)设定分值 +int PositionStruct::GenCapMoves(MoveStruct *lpmvs) const { + int i, sqSrc, sqDst, pcCaptured; + int x, y, nSideTag, nOppSideTag; + bool bCanPromote; + SlideMoveStruct *lpsmv; + uint8_t *lpucsqDst, *lpucsqPin; + MoveStruct *lpmvsCurr; + // 生成吃子着法的过程包括以下几个步骤: + + lpmvsCurr = lpmvs; + nSideTag = SIDE_TAG(sdPlayer); + nOppSideTag = OPP_SIDE_TAG(sdPlayer); + bCanPromote = PreEval.bPromotion && CanPromote(); + + // 1. 生成帅(将)的着法 + sqSrc = ucsqPieces[nSideTag + KING_FROM]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + lpucsqDst = PreGen.ucsqKingMoves[sqSrc]; + sqDst = *lpucsqDst; + while (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + // 找到一个着法后,首先判断吃到的棋子是否是对方棋子,技巧是利用"nOppSideTag"的标志(16和32颠倒), + // 如果是对方棋子,则保存MVV(LVA)值,即如果被吃子无保护,则只记MVV,否则记MVV-LVA(如果MVV>LVA的话)。 + pcCaptured = ucpcSquares[sqDst]; + if ((pcCaptured & nOppSideTag) != 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->wmv = MOVE(sqSrc, sqDst); + lpmvsCurr->wvl = MvvLva(sqDst, pcCaptured, 5); // 帅(将)的价值是5 + lpmvsCurr ++; + } + lpucsqDst ++; + sqDst = *lpucsqDst; + } + } + + // 2. 生成仕(士)的着法 + for (i = ADVISOR_FROM; i <= ADVISOR_TO; i ++) { + sqSrc = ucsqPieces[nSideTag + i]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + lpucsqDst = PreGen.ucsqAdvisorMoves[sqSrc]; + sqDst = *lpucsqDst; + while (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + pcCaptured = ucpcSquares[sqDst]; + if ((pcCaptured & nOppSideTag) != 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->wmv = MOVE(sqSrc, sqDst); + lpmvsCurr->wvl = MvvLva(sqDst, pcCaptured, 1); // 仕(士)的价值是1 + lpmvsCurr ++; + } + lpucsqDst ++; + sqDst = *lpucsqDst; + } + if (bCanPromote && CAN_PROMOTE(sqSrc)) { + lpmvsCurr->wmv = MOVE(sqSrc, sqSrc); + lpmvsCurr->wvl = 0; + lpmvsCurr ++; + } + } + } + + // 3. 生成相(象)的着法 + for (i = BISHOP_FROM; i <= BISHOP_TO; i ++) { + sqSrc = ucsqPieces[nSideTag + i]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + lpucsqDst = PreGen.ucsqBishopMoves[sqSrc]; + lpucsqPin = PreGen.ucsqBishopPins[sqSrc]; + sqDst = *lpucsqDst; + while (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + if (ucpcSquares[*lpucsqPin] == 0) { + pcCaptured = ucpcSquares[sqDst]; + if ((pcCaptured & nOppSideTag) != 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->wmv = MOVE(sqSrc, sqDst); + lpmvsCurr->wvl = MvvLva(sqDst, pcCaptured, 1); // 相(象)的价值是1 + lpmvsCurr ++; + } + } + lpucsqDst ++; + sqDst = *lpucsqDst; + lpucsqPin ++; + } + if (bCanPromote && CAN_PROMOTE(sqSrc)) { + lpmvsCurr->wmv = MOVE(sqSrc, sqSrc); + lpmvsCurr->wvl = 0; + lpmvsCurr ++; + } + } + } + + // 4. 生成马的着法 + for (i = KNIGHT_FROM; i <= KNIGHT_TO; i ++) { + sqSrc = ucsqPieces[nSideTag + i]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + lpucsqDst = PreGen.ucsqKnightMoves[sqSrc]; + lpucsqPin = PreGen.ucsqKnightPins[sqSrc]; + sqDst = *lpucsqDst; + while (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + if (ucpcSquares[*lpucsqPin] == 0) { + pcCaptured = ucpcSquares[sqDst]; + if ((pcCaptured & nOppSideTag) != 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->wmv = MOVE(sqSrc, sqDst); + lpmvsCurr->wvl = MvvLva(sqDst, pcCaptured, 3); // 马的价值是3 + lpmvsCurr ++; + } + } + lpucsqDst ++; + sqDst = *lpucsqDst; + lpucsqPin ++; + } + } + } + + // 5. 生成车的着法 + for (i = ROOK_FROM; i <= ROOK_TO; i ++) { + sqSrc = ucsqPieces[nSideTag + i]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + x = FILE_X(sqSrc); + y = RANK_Y(sqSrc); + + lpsmv = RankMovePtr(x, y); + sqDst = lpsmv->ucRookCap[0] + RANK_DISP(y); + __ASSERT_SQUARE(sqDst); + if (sqDst != sqSrc) { + pcCaptured = ucpcSquares[sqDst]; + if ((pcCaptured & nOppSideTag) != 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->wmv = MOVE(sqSrc, sqDst); + lpmvsCurr->wvl = MvvLva(sqDst, pcCaptured, 4); // 车的价值是4 + lpmvsCurr ++; + } + } + sqDst = lpsmv->ucRookCap[1] + RANK_DISP(y); + __ASSERT_SQUARE(sqDst); + if (sqDst != sqSrc) { + pcCaptured = ucpcSquares[sqDst]; + if ((pcCaptured & nOppSideTag) != 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->wmv = MOVE(sqSrc, sqDst); + lpmvsCurr->wvl = MvvLva(sqDst, pcCaptured, 4); // 车的价值是4 + lpmvsCurr ++; + } + } + + lpsmv = FileMovePtr(x, y); + sqDst = lpsmv->ucRookCap[0] + FILE_DISP(x); + __ASSERT_SQUARE(sqDst); + if (sqDst != sqSrc) { + pcCaptured = ucpcSquares[sqDst]; + if ((pcCaptured & nOppSideTag) != 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->wmv = MOVE(sqSrc, sqDst); + lpmvsCurr->wvl = MvvLva(sqDst, pcCaptured, 4); // 车的价值是4 + lpmvsCurr ++; + } + } + sqDst = lpsmv->ucRookCap[1] + FILE_DISP(x); + __ASSERT_SQUARE(sqDst); + if (sqDst != sqSrc) { + pcCaptured = ucpcSquares[sqDst]; + if ((pcCaptured & nOppSideTag) != 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->wmv = MOVE(sqSrc, sqDst); + lpmvsCurr->wvl = MvvLva(sqDst, pcCaptured, 4); // 车的价值是4 + lpmvsCurr ++; + } + } + } + } + + // 6. 生成炮的着法 + for (i = CANNON_FROM; i <= CANNON_TO; i ++) { + sqSrc = ucsqPieces[nSideTag + i]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + x = FILE_X(sqSrc); + y = RANK_Y(sqSrc); + + lpsmv = RankMovePtr(x, y); + sqDst = lpsmv->ucCannonCap[0] + RANK_DISP(y); + __ASSERT_SQUARE(sqDst); + if (sqDst != sqSrc) { + pcCaptured = ucpcSquares[sqDst]; + if ((pcCaptured & nOppSideTag) != 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->wmv = MOVE(sqSrc, sqDst); + lpmvsCurr->wvl = MvvLva(sqDst, pcCaptured, 3); // 炮的价值是3 + lpmvsCurr ++; + } + } + sqDst = lpsmv->ucCannonCap[1] + RANK_DISP(y); + __ASSERT_SQUARE(sqDst); + if (sqDst != sqSrc) { + pcCaptured = ucpcSquares[sqDst]; + if ((pcCaptured & nOppSideTag) != 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->wmv = MOVE(sqSrc, sqDst); + lpmvsCurr->wvl = MvvLva(sqDst, pcCaptured, 3); // 炮的价值是3 + lpmvsCurr ++; + } + } + + lpsmv = FileMovePtr(x, y); + sqDst = lpsmv->ucCannonCap[0] + FILE_DISP(x); + __ASSERT_SQUARE(sqDst); + if (sqDst != sqSrc) { + pcCaptured = ucpcSquares[sqDst]; + if ((pcCaptured & nOppSideTag) != 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->wmv = MOVE(sqSrc, sqDst); + lpmvsCurr->wvl = MvvLva(sqDst, pcCaptured, 3); // 炮的价值是3 + lpmvsCurr ++; + } + } + sqDst = lpsmv->ucCannonCap[1] + FILE_DISP(x); + __ASSERT_SQUARE(sqDst); + if (sqDst != sqSrc) { + pcCaptured = ucpcSquares[sqDst]; + if ((pcCaptured & nOppSideTag) != 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->wmv = MOVE(sqSrc, sqDst); + lpmvsCurr->wvl = MvvLva(sqDst, pcCaptured, 3); // 炮的价值是3 + lpmvsCurr ++; + } + } + } + } + + // 7. 生成兵(卒)的着法 + for (i = PAWN_FROM; i <= PAWN_TO; i ++) { + sqSrc = ucsqPieces[nSideTag + i]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + lpucsqDst = PreGen.ucsqPawnMoves[sdPlayer][sqSrc]; + sqDst = *lpucsqDst; + while (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + pcCaptured = ucpcSquares[sqDst]; + if ((pcCaptured & nOppSideTag) != 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->wmv = MOVE(sqSrc, sqDst); + lpmvsCurr->wvl = MvvLva(sqDst, pcCaptured, 2); // 兵(卒)的价值是2 + lpmvsCurr ++; + } + lpucsqDst ++; + sqDst = *lpucsqDst; + } + } + } + return lpmvsCurr - lpmvs; +} + +// 不吃子着法生成器 +int PositionStruct::GenNonCapMoves(MoveStruct *lpmvs) const { + int i, sqSrc, sqDst, x, y, nSideTag; + SlideMoveStruct *lpsmv; + uint8_t *lpucsqDst, *lpucsqPin; + MoveStruct *lpmvsCurr; + // 生成不吃子着法的过程包括以下几个步骤: + + lpmvsCurr = lpmvs; + nSideTag = SIDE_TAG(sdPlayer); + + // 1. 生成帅(将)的着法 + sqSrc = ucsqPieces[nSideTag + KING_FROM]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + lpucsqDst = PreGen.ucsqKingMoves[sqSrc]; + sqDst = *lpucsqDst; + while (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + // 找到一个着法后,首先判断是否吃到棋子 + if (ucpcSquares[sqDst] == 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->dwmv = MOVE(sqSrc, sqDst); + lpmvsCurr ++; + } + lpucsqDst ++; + sqDst = *lpucsqDst; + } + } + + // 2. 生成仕(士)的着法 + for (i = ADVISOR_FROM; i <= ADVISOR_TO; i ++) { + sqSrc = ucsqPieces[nSideTag + i]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + lpucsqDst = PreGen.ucsqAdvisorMoves[sqSrc]; + sqDst = *lpucsqDst; + while (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + if (ucpcSquares[sqDst] == 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->dwmv = MOVE(sqSrc, sqDst); + lpmvsCurr ++; + } + lpucsqDst ++; + sqDst = *lpucsqDst; + } + } + } + + // 3. 生成相(象)的着法 + for (i = BISHOP_FROM; i <= BISHOP_TO; i ++) { + sqSrc = ucsqPieces[nSideTag + i]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + lpucsqDst = PreGen.ucsqBishopMoves[sqSrc]; + lpucsqPin = PreGen.ucsqBishopPins[sqSrc]; + sqDst = *lpucsqDst; + while (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + if (ucpcSquares[*lpucsqPin] == 0 && ucpcSquares[sqDst] == 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->dwmv = MOVE(sqSrc, sqDst); + lpmvsCurr ++; + } + lpucsqDst ++; + sqDst = *lpucsqDst; + lpucsqPin ++; + } + } + } + + // 4. 生成马的着法 + for (i = KNIGHT_FROM; i <= KNIGHT_TO; i ++) { + sqSrc = ucsqPieces[nSideTag + i]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + lpucsqDst = PreGen.ucsqKnightMoves[sqSrc]; + lpucsqPin = PreGen.ucsqKnightPins[sqSrc]; + sqDst = *lpucsqDst; + while (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + if (ucpcSquares[*lpucsqPin] == 0 && ucpcSquares[sqDst] == 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->dwmv = MOVE(sqSrc, sqDst); + lpmvsCurr ++; + } + lpucsqDst ++; + sqDst = *lpucsqDst; + lpucsqPin ++; + } + } + } + + // 5. 生成车和炮的着法,没有必要判断是否吃到本方棋子 + for (i = ROOK_FROM; i <= CANNON_TO; i ++) { + sqSrc = ucsqPieces[nSideTag + i]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + x = FILE_X(sqSrc); + y = RANK_Y(sqSrc); + + lpsmv = RankMovePtr(x, y); + sqDst = lpsmv->ucNonCap[0] + RANK_DISP(y); + __ASSERT_SQUARE(sqDst); + while (sqDst != sqSrc) { + __ASSERT(ucpcSquares[sqDst] == 0); + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->dwmv = MOVE(sqSrc, sqDst); + lpmvsCurr ++; + sqDst --; + } + sqDst = lpsmv->ucNonCap[1] + RANK_DISP(y); + __ASSERT_SQUARE(sqDst); + while (sqDst != sqSrc) { + __ASSERT(ucpcSquares[sqDst] == 0); + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->dwmv = MOVE(sqSrc, sqDst); + lpmvsCurr ++; + sqDst ++; + } + + lpsmv = FileMovePtr(x, y); + sqDst = lpsmv->ucNonCap[0] + FILE_DISP(x); + __ASSERT_SQUARE(sqDst); + while (sqDst != sqSrc) { + __ASSERT(ucpcSquares[sqDst] == 0); + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->dwmv = MOVE(sqSrc, sqDst); + lpmvsCurr ++; + sqDst -= 16; + } + sqDst = lpsmv->ucNonCap[1] + FILE_DISP(x); + __ASSERT_SQUARE(sqDst); + while (sqDst != sqSrc) { + __ASSERT(ucpcSquares[sqDst] == 0); + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->dwmv = MOVE(sqSrc, sqDst); + lpmvsCurr ++; + sqDst += 16; + } + } + } + + // 6. 生成兵(卒)的着法 + for (i = PAWN_FROM; i <= PAWN_TO; i ++) { + sqSrc = ucsqPieces[nSideTag + i]; + if (sqSrc != 0) { + __ASSERT_SQUARE(sqSrc); + lpucsqDst = PreGen.ucsqPawnMoves[sdPlayer][sqSrc]; + sqDst = *lpucsqDst; + while (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + if (ucpcSquares[sqDst] == 0) { + __ASSERT(LegalMove(MOVE(sqSrc, sqDst))); + lpmvsCurr->dwmv = MOVE(sqSrc, sqDst); + lpmvsCurr ++; + } + lpucsqDst ++; + sqDst = *lpucsqDst; + } + } + } + return lpmvsCurr - lpmvs; +} + +// “捉”的检测 +int PositionStruct::ChasedBy(int mv) const { + int i, nSideTag, pcMoved, pcCaptured; + int sqSrc, sqDst, x, y; + uint8_t *lpucsqDst, *lpucsqPin; + SlideMoveStruct *lpsmv; + + sqSrc = DST(mv); + pcMoved = this->ucpcSquares[sqSrc]; + nSideTag = SIDE_TAG(this->sdPlayer); + __ASSERT_SQUARE(sqSrc); + __ASSERT_PIECE(pcMoved); + __ASSERT_BOUND(0, pcMoved - OPP_SIDE_TAG(this->sdPlayer), 15); + + // “捉”的判断包括以下几部分内容: + switch (pcMoved - OPP_SIDE_TAG(this->sdPlayer)) { + + // 1. 走了马,判断是否捉车或捉有根的炮兵(卒) + case KNIGHT_FROM: + case KNIGHT_TO: + // 逐一检测马踩的八个位置 + lpucsqDst = PreGen.ucsqKnightMoves[sqSrc]; + lpucsqPin = PreGen.ucsqKnightPins[sqSrc]; + sqDst = *lpucsqDst; + while (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + if (ucpcSquares[*lpucsqPin] == 0) { + pcCaptured = this->ucpcSquares[sqDst]; + if ((pcCaptured & nSideTag) != 0) { + pcCaptured -= nSideTag; + __ASSERT_BOUND(0, pcCaptured, 15); + // 技巧:优化兵种判断的分枝 + if (pcCaptured <= ROOK_TO) { + // 马捉仕(士)、相(象)和马的情况不予考虑 + if (pcCaptured >= ROOK_FROM) { + // 马捉到了车 + return pcCaptured; + } + } else { + if (pcCaptured <= CANNON_TO) { + // 马捉到了炮,要判断炮是否受保护 + if (!Protected(this->sdPlayer, sqDst)) { + return pcCaptured; + } + } else { + // 马捉到了兵(卒),要判断兵(卒)是否过河并受保护 + if (AWAY_HALF(sqDst, sdPlayer) && !Protected(this->sdPlayer, sqDst)) { + return pcCaptured; + } + } + } + } + } + lpucsqDst ++; + sqDst = *lpucsqDst; + lpucsqPin ++; + } + break; + + // 2. 走了车,判断是否捉有根的马炮兵(卒) + case ROOK_FROM: + case ROOK_TO: + x = FILE_X(sqSrc); + y = RANK_Y(sqSrc); + if (((SRC(mv) ^ sqSrc) & 0xf) == 0) { + // 如果车纵向移动了,则判断车横向吃到的子 + lpsmv = RankMovePtr(x, y); + for (i = 0; i < 2; i ++) { + sqDst = lpsmv->ucRookCap[i] + RANK_DISP(y); + __ASSERT_SQUARE(sqDst); + if (sqDst != sqSrc) { + pcCaptured = this->ucpcSquares[sqDst]; + if ((pcCaptured & nSideTag) != 0) { + pcCaptured -= nSideTag; + __ASSERT_BOUND(0, pcCaptured, 15); + // 技巧:优化兵种判断的分枝 + if (pcCaptured <= ROOK_TO) { + // 车捉仕(士)、相(象)的情况不予考虑 + if (pcCaptured >= KNIGHT_FROM) { + if (pcCaptured <= KNIGHT_TO) { + // 车捉到了马,要判断马是否受保护 + if (!Protected(this->sdPlayer, sqDst)) { + return pcCaptured; + } + } + // 车捉车的情况不予考虑 + } + } else { + if (pcCaptured <= CANNON_TO) { + // 车捉到了炮,要判断炮是否受保护 + if (!Protected(this->sdPlayer, sqDst)) { + return pcCaptured; + } + } else { + // 车捉到了兵(卒),要判断兵(卒)是否过河并受保护 + if (AWAY_HALF(sqDst, sdPlayer) && !Protected(this->sdPlayer, sqDst)) { + return pcCaptured; + } + } + } + } + } + } + } else { + // 如果车横向移动了,则判断车纵向吃到的子 + lpsmv = FileMovePtr(x, y); + for (i = 0; i < 2; i ++) { + sqDst = lpsmv->ucRookCap[i] + FILE_DISP(x); + __ASSERT_SQUARE(sqDst); + if (sqDst != sqSrc) { + pcCaptured = this->ucpcSquares[sqDst]; + if ((pcCaptured & nSideTag) != 0) { + pcCaptured -= nSideTag; + __ASSERT_BOUND(0, pcCaptured, 15); + // 技巧:优化兵种判断的分枝 + if (pcCaptured <= ROOK_TO) { + // 车捉仕(士)、相(象)的情况不予考虑 + if (pcCaptured >= KNIGHT_FROM) { + if (pcCaptured <= KNIGHT_TO) { + // 车捉到了马,要判断马是否受保护 + if (!Protected(this->sdPlayer, sqDst)) { + return pcCaptured; + } + } + // 车捉车的情况不予考虑 + } + } else { + if (pcCaptured <= CANNON_TO) { + // 车捉到了炮,要判断炮是否受保护 + if (!Protected(this->sdPlayer, sqDst)) { + return pcCaptured; + } + } else { + // 车捉到了兵(卒),要判断兵(卒)是否过河并受保护 + if (AWAY_HALF(sqDst, sdPlayer) && !Protected(this->sdPlayer, sqDst)) { + return pcCaptured; + } + } + } + } + } + } + } + break; + + // 3. 走了炮,判断是否捉车或捉有根的马兵(卒) + case CANNON_FROM: + case CANNON_TO: + x = FILE_X(sqSrc); + y = RANK_Y(sqSrc); + if (((SRC(mv) ^ sqSrc) & 0xf) == 0) { + // 如果炮纵向移动了,则判断炮横向吃到的子 + lpsmv = RankMovePtr(x, y); + for (i = 0; i < 2; i ++) { + sqDst = lpsmv->ucCannonCap[i] + RANK_DISP(y); + __ASSERT_SQUARE(sqDst); + if (sqDst != sqSrc) { + pcCaptured = this->ucpcSquares[sqDst]; + if ((pcCaptured & nSideTag) != 0) { + pcCaptured -= nSideTag; + __ASSERT_BOUND(0, pcCaptured, 15); + // 技巧:优化兵种判断的分枝 + if (pcCaptured <= ROOK_TO) { + // 炮捉仕(士)、相(象)的情况不予考虑 + if (pcCaptured >= KNIGHT_FROM) { + if (pcCaptured <= KNIGHT_TO) { + // 炮捉到了马,要判断马是否受保护 + if (!Protected(this->sdPlayer, sqDst)) { + return pcCaptured; + } + } else { + // 炮捉到了车 + return pcCaptured; + } + } + } else { + // 炮捉炮的情况不予考虑 + if (pcCaptured >= PAWN_FROM) { + // 炮捉到了兵(卒),要判断兵(卒)是否过河并受保护 + if (AWAY_HALF(sqDst, sdPlayer) && !Protected(this->sdPlayer, sqDst)) { + return pcCaptured; + } + } + } + } + } + } + } else { + // 如果炮横向移动了,则判断炮纵向吃到的子 + lpsmv = FileMovePtr(x, y); + for (i = 0; i < 2; i ++) { + sqDst = lpsmv->ucCannonCap[i] + FILE_DISP(x); + __ASSERT_SQUARE(sqDst); + if (sqDst != sqSrc) { + pcCaptured = this->ucpcSquares[sqDst]; + if ((pcCaptured & nSideTag) != 0) { + pcCaptured -= nSideTag; + __ASSERT_BOUND(0, pcCaptured, 15); + // 技巧:优化兵种判断的分枝 + if (pcCaptured <= ROOK_TO) { + // 炮捉仕(士)、相(象)的情况不予考虑 + if (pcCaptured >= KNIGHT_FROM) { + if (pcCaptured <= KNIGHT_TO) { + // 炮捉到了马,要判断马是否受保护 + if (!Protected(this->sdPlayer, sqDst)) { + return pcCaptured; + } + } else { + // 炮捉到了车 + return pcCaptured; + } + } + } else { + // 炮捉炮的情况不予考虑 + if (pcCaptured >= PAWN_FROM) { + // 炮捉到了兵(卒),要判断兵(卒)是否过河并受保护 + if (AWAY_HALF(sqDst, sdPlayer) && !Protected(this->sdPlayer, sqDst)) { + return pcCaptured; + } + } + } + } + } + } + } + break; + } + + return 0; +} diff --git a/eleeye/hash.cpp b/eleeye/hash.cpp new file mode 100644 index 0000000..4911708 --- /dev/null +++ b/eleeye/hash.cpp @@ -0,0 +1,302 @@ +/* +hash.h/hash.cpp - Source Code for ElephantEye, Part V + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.12, Last Modified: Dec. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "../base/base.h" +#include "position.h" +#include "hash.h" + +int nHashMask; +HashStruct *hshItems; +#ifdef HASH_QUIESC + HashStruct *hshItemsQ; +#endif + +// 存储置换表局面信息 +void RecordHash(const PositionStruct &pos, int nFlag, int vl, int nDepth, int mv) { + HashStruct hsh; + int i, nHashDepth, nMinDepth, nMinLayer; + // 存储置换表局面信息的过程包括以下几个步骤: + + // 1. 对分值做杀棋步数调整; + __ASSERT_BOUND(1 - MATE_VALUE, vl, MATE_VALUE - 1); + __ASSERT(mv == 0 || pos.LegalMove(mv)); + if (vl > WIN_VALUE) { + if (mv == 0 && vl <= BAN_VALUE) { + return; // 导致长将的局面(不进行置换裁剪)如果连最佳着法也没有,那么没有必要写入置换表 + } + vl += pos.nDistance; + } else if (vl < -WIN_VALUE) { + if (mv == 0 && vl >= -BAN_VALUE) { + return; // 同理 + } + vl -= pos.nDistance; + } else if (vl == pos.DrawValue() && mv == 0) { + return; // 同理 + } + + // 2. 逐层试探置换表; + nMinDepth = 512; + nMinLayer = 0; + for (i = 0; i < HASH_LAYERS; i ++) { + hsh = HASH_ITEM(pos, i); + + // 3. 如果试探到一样的局面,那么更新置换表信息即可; + if (HASH_POS_EQUAL(hsh, pos)) { + // 如果深度更深,或者边界缩小,都可更新置换表的值 + if ((nFlag & HASH_ALPHA) != 0 && (hsh.ucAlphaDepth <= nDepth || hsh.svlAlpha >= vl)) { + hsh.ucAlphaDepth = nDepth; + hsh.svlAlpha = vl; + } + // Beta结点要注意:不要用Null-Move的结点覆盖正常的结点 + if ((nFlag & HASH_BETA) != 0 && (hsh.ucBetaDepth <= nDepth || hsh.svlBeta <= vl) && (mv != 0 || hsh.wmv == 0)) { + hsh.ucBetaDepth = nDepth; + hsh.svlBeta = vl; + } + // 最佳着法是始终覆盖的 + if (mv != 0) { + hsh.wmv = mv; + } + HASH_ITEM(pos, i) = hsh; + return; + } + + // 4. 如果不是一样的局面,那么获得深度最小的置换表项; + nHashDepth = MAX((hsh.ucAlphaDepth == 0 ? 0 : hsh.ucAlphaDepth + 256), + (hsh.wmv == 0 ? hsh.ucBetaDepth : hsh.ucBetaDepth + 256)); + __ASSERT(nHashDepth < 512); + if (nHashDepth < nMinDepth) { + nMinDepth = nHashDepth; + nMinLayer = i; + } + } + + // 5. 记录置换表。 + hsh.dwZobristLock0 = pos.zobr.dwLock0; + hsh.dwZobristLock1 = pos.zobr.dwLock1; + hsh.wmv = mv; + hsh.ucAlphaDepth = hsh.ucBetaDepth = 0; + hsh.svlAlpha = hsh.svlBeta = 0; + if ((nFlag & HASH_ALPHA) != 0) { + hsh.ucAlphaDepth = nDepth; + hsh.svlAlpha = vl; + } + if ((nFlag & HASH_BETA) != 0) { + hsh.ucBetaDepth = nDepth; + hsh.svlBeta = vl; + } + HASH_ITEM(pos, nMinLayer) = hsh; +} + +/* 判断获取置换表要符合哪些条件,置换表的分值针对四个不同的区间有不同的处理: + * 一、如果分值在"WIN_VALUE"以内(即介于"-WIN_VALUE"到"WIN_VALUE"之间,下同),则只获取满足搜索深度要求的局面; + * 二、如果分值在"WIN_VALUE"和"BAN_VALUE"之间,则不能获取置换表中的值(只能获取最佳着法仅供参考),目的是防止由于长将而导致的“置换表的不稳定性”; + * 三、如果分值在"BAN_VALUE"以外,则获取局面时不必考虑搜索深度要求,因为这些局面已经被证明是杀棋了; + * 四、如果分值是"DrawValue()"(是第一种情况的特殊情况),则不能获取置换表中的值(原因与第二种情况相同)。 + * 注意:对于第三种情况,要对杀棋步数进行调整! + */ +inline int ValueAdjust(const PositionStruct &pos, bool &bBanNode, bool &bMateNode, int vl) { + bBanNode = bMateNode = false; + if (vl > WIN_VALUE) { + if (vl <= BAN_VALUE) { + bBanNode = true; + } else { + bMateNode = true; + vl -= pos.nDistance; + } + } else if (vl < -WIN_VALUE) { + if (vl >= -BAN_VALUE) { + bBanNode = true; + } else { + bMateNode = true; + vl += pos.nDistance; + } + } else if (vl == pos.DrawValue()) { + bBanNode = true; + } + return vl; +} + +// 检测下一个着法是否稳定,有助于减少置换表的不稳定性 +inline bool MoveStable(PositionStruct &pos, int mv) { + // 判断下一个着法是否稳定的依据是: + // 1. 没有后续着法,则假定是稳定的; + if (mv == 0) { + return true; + } + // 2. 吃子着法是稳定的; + __ASSERT(pos.LegalMove(mv)); + if (pos.ucpcSquares[DST(mv)] != 0) { + return true; + } + // 3. 可能因置换表引起路线迁移,使得路线超过"MAX_MOVE_NUM",此时应立刻终止路线,并假定是稳定的。 + if (!pos.MakeMove(mv)) { + return true; + } + return false; +} + +// 检测后续路线是否稳定(不是循环路线),有助于减少置换表的不稳定性 +static bool PosStable(const PositionStruct &pos, int mv) { + HashStruct hsh; + int i, nMoveNum; + bool bStable; + // pos会沿着路线变化,但最终会还原,所以被视为"const",而让"posMutable"承担非"const"的角色 + PositionStruct &posMutable = (PositionStruct &) pos; + + __ASSERT(mv != 0); + nMoveNum = 0; + bStable = true; + while (!MoveStable(posMutable, mv)) { + nMoveNum ++; // "!MoveStable()"表明已经执行了一个着法,以后需要撤消 + // 执行这个着法,如果产生循环,那么终止后续路线,并确认该路线不稳定 + if (posMutable.RepStatus() > 0) { + bStable = false; + break; + } + // 逐层获取置换表项,方法同"ProbeHash()" + for (i = 0; i < HASH_LAYERS; i ++) { + hsh = HASH_ITEM(posMutable, i); + if (HASH_POS_EQUAL(hsh, posMutable)) { + break; + } + } + mv = (i == HASH_LAYERS ? 0 : hsh.wmv); + } + // 撤消前面执行过的所有着法 + for (i = 0; i < nMoveNum; i ++) { + posMutable.UndoMakeMove(); + } + return bStable; +} + +// 获取置换表局面信息(没有命中时,返回"-MATE_VALUE") +int ProbeHash(const PositionStruct &pos, int vlAlpha, int vlBeta, int nDepth, bool bNoNull, int &mv) { + HashStruct hsh; + int i, vl; + bool bBanNode, bMateNode; + // 获取置换表局面信息的过程包括以下几个步骤: + + // 1. 逐层获取置换表项 + mv = 0; + for (i = 0; i < HASH_LAYERS; i ++) { + hsh = HASH_ITEM(pos, i); + if (HASH_POS_EQUAL(hsh, pos)) { + mv = hsh.wmv; + __ASSERT(mv == 0 || pos.LegalMove(mv)); + break; + } + } + if (i == HASH_LAYERS) { + return -MATE_VALUE; + } + + // 2. 判断是否符合Beta边界 + if (hsh.ucBetaDepth > 0) { + vl = ValueAdjust(pos, bBanNode, bMateNode, hsh.svlBeta); + if (!bBanNode && !(hsh.wmv == 0 && bNoNull) && (hsh.ucBetaDepth >= nDepth || bMateNode) && vl >= vlBeta) { + __ASSERT_BOUND(1 - MATE_VALUE, vl, MATE_VALUE - 1); + if (hsh.wmv == 0 || PosStable(pos, hsh.wmv)) { + return vl; + } + } + } + + // 3. 判断是否符合Alpha边界 + if (hsh.ucAlphaDepth > 0) { + vl = ValueAdjust(pos, bBanNode, bMateNode, hsh.svlAlpha); + if (!bBanNode && (hsh.ucAlphaDepth >= nDepth || bMateNode) && vl <= vlAlpha) { + __ASSERT_BOUND(1 - MATE_VALUE, vl, MATE_VALUE - 1); + if (hsh.wmv == 0 || PosStable(pos, hsh.wmv)) { + return vl; + } + } + } + return -MATE_VALUE; +} + +#ifdef HASH_QUIESC + +// 存储置换表局面信息(静态搜索) +void RecordHashQ(const PositionStruct &pos, int vlBeta, int vlAlpha) { + volatile HashStruct *lphsh; + __ASSERT((vlBeta > -WIN_VALUE && vlBeta < WIN_VALUE) || (vlAlpha > -WIN_VALUE && vlAlpha < WIN_VALUE)); + lphsh = hshItemsQ + (pos.zobr.dwKey & nHashMask); + lphsh->dwZobristLock0 = pos.zobr.dwLock0; + lphsh->svlAlpha = vlAlpha; + lphsh->svlBeta = vlBeta; + lphsh->dwZobristLock1 = pos.zobr.dwLock1; +} + +// 获取置换表局面信息(静态搜索) +int ProbeHashQ(const PositionStruct &pos, int vlAlpha, int vlBeta) { + volatile HashStruct *lphsh; + int vlHashAlpha, vlHashBeta; + + lphsh = hshItemsQ + (pos.zobr.dwKey & nHashMask); + if (lphsh->dwZobristLock0 == pos.zobr.dwLock0) { + vlHashAlpha = lphsh->svlAlpha; + vlHashBeta = lphsh->svlBeta; + if (lphsh->dwZobristLock1 == pos.zobr.dwLock1) { + if (vlHashBeta >= vlBeta) { + __ASSERT(vlHashBeta > -WIN_VALUE && vlHashBeta < WIN_VALUE); + return vlHashBeta; + } + if (vlHashAlpha <= vlAlpha) { + __ASSERT(vlHashAlpha > -WIN_VALUE && vlHashAlpha < WIN_VALUE); + return vlHashAlpha; + } + } + } + return -MATE_VALUE; +} + +#endif + +// UCCI支持 - 输出Hash表中的局面信息 +bool PopHash(const PositionStruct &pos) { + HashStruct hsh; + uint32_t dwMoveStr; + int i; + + for (i = 0; i < HASH_LAYERS; i ++) { + hsh = HASH_ITEM(pos, i); + if (HASH_POS_EQUAL(hsh, pos)) { + printf("pophash"); + if (hsh.wmv != 0) { + __ASSERT(pos.LegalMove(hsh.wmv)); + dwMoveStr = MOVE_COORD(hsh.wmv); + printf(" bestmove %.4s", (const char *) &dwMoveStr); + } + if (hsh.ucBetaDepth > 0) { + printf(" lowerbound %d depth %d", hsh.svlBeta, hsh.ucBetaDepth); + } + if (hsh.ucAlphaDepth > 0) { + printf(" upperbound %d depth %d", hsh.svlAlpha, hsh.ucAlphaDepth); + } + printf("\n"); + fflush(stdout); + return true; + } + } + return false; +} diff --git a/eleeye/hash.h b/eleeye/hash.h new file mode 100644 index 0000000..973f6a1 --- /dev/null +++ b/eleeye/hash.h @@ -0,0 +1,98 @@ +/* +hash.h/hash.cpp - Source Code for ElephantEye, Part V + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.12, Last Modified: Dec. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "../base/base.h" +#include "position.h" + +#ifndef HASH_H +#define HASH_H + +// 置换表标志,只用在"RecordHash()"函数中 +const int HASH_BETA = 1; +const int HASH_ALPHA = 2; +const int HASH_PV = HASH_ALPHA | HASH_BETA; + +const int HASH_LAYERS = 2; // 置换表的层数 +const int NULL_DEPTH = 2; // 空着裁剪的深度 + +// 置换表结构,置换表信息夹在两个Zobrist校验锁中间,可以防止存取冲突 +struct HashStruct { + uint32_t dwZobristLock0; // Zobrist校验锁,第一部分 + uint16_t wmv; // 最佳着法 + uint8_t ucAlphaDepth, ucBetaDepth; // 深度(上边界和下边界) + int16_t svlAlpha, svlBeta; // 分值(上边界和下边界) + uint32_t dwZobristLock1; // Zobrist校验锁,第二部分 +}; // hsh + +// 置换表信息 +extern int nHashMask; // 置换表的大小 +extern HashStruct *hshItems; // 置换表的指针,ElephantEye采用多层的置换表 +#ifdef HASH_QUIESC + extern HashStruct *hshItemsQ; +#endif + +inline void ClearHash(void) { // 清空置换表 + memset(hshItems, 0, (nHashMask + 1) * sizeof(HashStruct)); +#ifdef HASH_QUIESC + memset(hshItemsQ, 0, (nHashMask + 1) * sizeof(HashStruct)); +#endif +} + +inline void NewHash(int nHashScale) { // 分配置换表,大小是 2^nHashScale 字节 + nHashMask = ((1 << nHashScale) / sizeof(HashStruct)) - 1; + hshItems = new HashStruct[nHashMask + 1]; +#ifdef HASH_QUIESC + hshItemsQ = new HashStruct[nHashMask + 1]; +#endif + ClearHash(); +} + +inline void DelHash(void) { // 释放置换表 + delete[] hshItems; +#ifdef HASH_QUIESC + delete[] hshItemsQ; +#endif +} + +// 判断置换表是否符合局面(Zobrist锁是否相等) +inline bool HASH_POS_EQUAL(const HashStruct &hsh, const PositionStruct &pos) { + return hsh.dwZobristLock0 == pos.zobr.dwLock0 && hsh.dwZobristLock1 == pos.zobr.dwLock1; +} + +// 按局面和层数获取置换表项(返回一个引用,可以对其赋值) +inline HashStruct &HASH_ITEM(const PositionStruct &pos, int nLayer) { + return hshItems[(pos.zobr.dwKey + nLayer) & nHashMask]; +} + +// 置换表的管理过程 +void RecordHash(const PositionStruct &pos, int nFlag, int vl, int nDepth, int mv); // 存储置换表局面信息 +int ProbeHash(const PositionStruct &pos, int vlAlpha, int vlBeta, int nDepth, bool bNoNull, int &mv); // 获取置换表局面信息 +#ifdef HASH_QUIESC + void RecordHashQ(const PositionStruct &pos, int vlBeta, int vlAlpha); // 存储置换表局面信息(静态搜索) + int ProbeHashQ(const PositionStruct &pos, int vlAlpha, int vlBeta); // 获取置换表局面信息(静态搜索) +#endif + +// UCCI支持 - 输出Hash表中的局面信息 +bool PopHash(const PositionStruct &pos); + +#endif diff --git a/eleeye/makefile.sh b/eleeye/makefile.sh new file mode 100644 index 0000000..cce753d --- /dev/null +++ b/eleeye/makefile.sh @@ -0,0 +1 @@ +g++ -DNDEBUG -O4 -Wall -ldl -oELEEYE.EXE ../base/pipe.cpp ucci.cpp pregen.cpp position.cpp genmoves.cpp hash.cpp book.cpp movesort.cpp search.cpp eleeye.cpp \ No newline at end of file diff --git a/eleeye/movesort.cpp b/eleeye/movesort.cpp new file mode 100644 index 0000000..26a25ec --- /dev/null +++ b/eleeye/movesort.cpp @@ -0,0 +1,209 @@ +/* +movesort.h/movesort.cpp - Source Code for ElephantEye, Part VII + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.11, Last Modified: Dec. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../base/base.h" +#include "../base/x86asm.h" +#include "position.h" +#include "movesort.h" + +int nHistory[65536]; // 历史表 + +// 根据历史表对着法列表赋值 +void MoveSortStruct::SetHistory(void) { + int i, j, vl, nShift, nNewShift; + nShift = 0; + for (i = nMoveIndex; i < nMoveNum; i ++) { + // 如果着法的分值超过65536,就必需对所有着法的分值作缩减,使它们都不超过65536 + vl = nHistory[mvs[i].wmv] >> nShift; + if (vl > 65535) { + nNewShift = Bsr(vl) - 15; + for (j = nMoveIndex; j < i; j ++) { + mvs[j].wvl >>= nNewShift; + } + vl >>= nNewShift; + __ASSERT_BOUND(32768, vl, 65535); + nShift += nNewShift; + } + mvs[i].wvl = vl; + } +} + +// 剥壳排序法,这里用"1, 4, 13, 40 ..."的序列,这样要比"1, 2, 4, 8, ..."好 +static const int cnShellStep[8] = {0, 1, 4, 13, 40, 121, 364, 1093}; + +void MoveSortStruct::ShellSort(void) { + int i, j, nStep, nStepLevel; + MoveStruct mvsBest; + nStepLevel = 1; + while (cnShellStep[nStepLevel] < nMoveNum - nMoveIndex) { + nStepLevel ++; + } + nStepLevel --; + while (nStepLevel > 0) { + nStep = cnShellStep[nStepLevel]; + for (i = nMoveIndex + nStep; i < nMoveNum; i ++) { + mvsBest = mvs[i]; + j = i - nStep; + while (j >= nMoveIndex && mvsBest.wvl > mvs[j].wvl) { + mvs[j + nStep] = mvs[j]; + j -= nStep; + } + mvs[j + nStep] = mvsBest; + } + nStepLevel --; + } +} + +/* 生成解将着法,返回唯一应将着法(有多个应将着法则返回零) + * + * 解将着法的顺序如下: + * 1. 置换表着法(SORT_VALUE_MAX); + * 2. 两个杀手着法(SORT_VALUE_MAX - 1或2); + * 3. 其他着法,按历史表排序(从1到SORT_VALUE_MAX - 3); + * 4. 不能解将的着法(0),这些着法会过滤掉。 + */ +int MoveSortStruct::InitEvade(PositionStruct &pos, int mv, const uint16_t *lpwmvKiller) { + int i, nLegal; + nPhase = PHASE_REST; + nMoveIndex = 0; + nMoveNum = pos.GenAllMoves(mvs); + SetHistory(); + nLegal = 0; + for (i = nMoveIndex; i < nMoveNum; i ++) { + if (mvs[i].wmv == mv) { + nLegal ++; + mvs[i].wvl = SORT_VALUE_MAX; + } else if (pos.MakeMove(mvs[i].wmv)) { + pos.UndoMakeMove(); + nLegal ++; + if (mvs[i].wmv == lpwmvKiller[0]) { + mvs[i].wvl = SORT_VALUE_MAX - 1; + } else if (mvs[i].wmv == lpwmvKiller[1]) { + mvs[i].wvl = SORT_VALUE_MAX - 2; + } else { + mvs[i].wvl = MIN(mvs[i].wvl + 1, SORT_VALUE_MAX - 3); + } + } else { + mvs[i].wvl = 0; + } + } + ShellSort(); + nMoveNum = nMoveIndex + nLegal; + return (nLegal == 1 ? mvs[0].wmv : 0); +} + +// 给出下一个即将搜索的着法 +int MoveSortStruct::NextFull(const PositionStruct &pos) { + switch (nPhase) { + // "nPhase"表示着法启发的若干阶段,依次为: + + // 0. 置换表着法启发,完成后立即进入下一阶段; + case PHASE_HASH: + nPhase = PHASE_GEN_CAP; + if (mvHash != 0) { + __ASSERT(pos.LegalMove(mvHash)); + return mvHash; + } + // 技巧:这里没有"break",表示"switch"的上一个"case"执行完后紧接着做下一个"case",下同 + + // 1. 生成所有吃子着法,完成后立即进入下一阶段; + case PHASE_GEN_CAP: + nPhase = PHASE_GOODCAP; + nMoveIndex = 0; + nMoveNum = pos.GenCapMoves(mvs); + ShellSort(); + + // 2. MVV(LVA)启发,可能要循环若干次; + case PHASE_GOODCAP: + if (nMoveIndex < nMoveNum && mvs[nMoveIndex].wvl > 1) { + // 注意:MVV(LVA)值不超过1,则说明吃子不是直接能获得优势的,这些着法将留在以后搜索 + nMoveIndex ++; + __ASSERT_PIECE(pos.ucpcSquares[DST(mvs[nMoveIndex - 1].wmv)]); + return mvs[nMoveIndex - 1].wmv; + } + + // 3. 杀手着法启发(第一个杀手着法),完成后立即进入下一阶段; + case PHASE_KILLER1: + nPhase = PHASE_KILLER2; + if (mvKiller1 != 0 && pos.LegalMove(mvKiller1)) { + // 注意:杀手着法必须检验着法合理性,下同 + return mvKiller1; + } + + // 4. 杀手着法启发(第二个杀手着法),完成后立即进入下一阶段; + case PHASE_KILLER2: + nPhase = PHASE_GEN_NONCAP; + if (mvKiller2 != 0 && pos.LegalMove(mvKiller2)) { + return mvKiller2; + } + + // 5. 生成所有不吃子着法,完成后立即进入下一阶段; + case PHASE_GEN_NONCAP: + nPhase = PHASE_REST; + nMoveNum += pos.GenNonCapMoves(mvs + nMoveNum); + SetHistory(); + ShellSort(); + + // 6. 对剩余着法做历史表启发(包括返回解将着法); + case PHASE_REST: + if (nMoveIndex < nMoveNum) { + nMoveIndex ++; + return mvs[nMoveIndex - 1].wmv; + } + + // 7. 没有着法了,返回零。 + default: + return 0; + } +} + +// 生成根结点的着法 +void MoveSortStruct::InitRoot(const PositionStruct &pos, int nBanMoves, const uint16_t *lpwmvBanList) { + int i, j, nBanned; + nMoveIndex = 0; + nMoveNum = pos.GenAllMoves(mvs); + nBanned = 0; + for (i = 0; i < nMoveNum; i ++) { + mvs[i].wvl = 1; + for (j = 0; j < nBanMoves; j ++) { + if (mvs[i].wmv == lpwmvBanList[j]) { + mvs[i].wvl = 0; + nBanned ++; + break; + } + } + } + ShellSort(); + nMoveNum -= nBanned; +} + +// 更新根结点的着法排序列表 +void MoveSortStruct::UpdateRoot(int mv) { + int i; + for (i = 0; i < nMoveNum; i ++) { + if (mvs[i].wmv == mv) { + mvs[i].wvl = SORT_VALUE_MAX; + } else if (mvs[i].wvl > 0) { + mvs[i].wvl --; + } + } +} diff --git a/eleeye/movesort.h b/eleeye/movesort.h new file mode 100644 index 0000000..5671a2b --- /dev/null +++ b/eleeye/movesort.h @@ -0,0 +1,149 @@ +/* +movesort.h/movesort.cpp - Source Code for ElephantEye, Part VII + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.11, Last Modified: Dec. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MOVESORT_H +#define MOVESORT_H + +#include +#include "../base/base.h" +#include "position.h" + +const int LIMIT_DEPTH = 64; // 搜索的极限深度 +const int SORT_VALUE_MAX = 65535; // 着法序列最大值 + +extern const int FIBONACCI_LIST[32]; + +// "nHistory"只在"movesort.cpp"一个模块中使用 +extern int nHistory[65536]; // 历史表 + +// 着法顺序的若干阶段(参阅"NextFull()"函数) +const int PHASE_HASH = 0; +const int PHASE_GEN_CAP = 1; +const int PHASE_GOODCAP = 2; +const int PHASE_KILLER1 = 3; +const int PHASE_KILLER2 = 4; +const int PHASE_GEN_NONCAP = 5; +const int PHASE_REST = 6; + +const bool NEXT_ALL = true; // 着法顺序函数"MoveSortStruct::NextQuiesc()"选项 +const bool ROOT_UNIQUE = true; // 着法顺序函数"MoveSortStruct::ResetRoot()"选项 + +// 着法序列结构 +struct MoveSortStruct { + int nPhase, nMoveIndex, nMoveNum; + int mvHash, mvKiller1, mvKiller2; + MoveStruct mvs[MAX_GEN_MOVES]; + + void SetHistory(void); // 根据历史表对着法列表赋值 + void ShellSort(void); // 着法排序过程 + // 好的吃子着法(包括没有着法,都不更新历史表和杀手着法表) + bool GoodCap(const PositionStruct &pos, int mv) { + return mv == 0 || nPhase == PHASE_GOODCAP || (nPhase < PHASE_GOODCAP && pos.GoodCap(mv)); + } + + // 静态搜索的着法顺序控制 + void InitAll(const PositionStruct &pos) { + nMoveIndex = 0; + nMoveNum = pos.GenAllMoves(mvs); + SetHistory(); + ShellSort(); + } + void InitQuiesc(const PositionStruct &pos) { + nMoveIndex = 0; + nMoveNum = pos.GenCapMoves(mvs); + ShellSort(); + } + void InitQuiesc2(const PositionStruct &pos) { + nMoveNum += pos.GenNonCapMoves(mvs); + SetHistory(); + ShellSort(); + } + int NextQuiesc(bool bNextAll = false) { + if (nMoveIndex < nMoveNum && (bNextAll || mvs[nMoveIndex].wvl > 0)) { + nMoveIndex ++; + return mvs[nMoveIndex - 1].wmv; + } else { + return 0; + } + } + + // 完全搜索的着法顺序控制 + void InitFull(const PositionStruct &pos, int mv, const uint16_t *lpwmvKiller) { + nPhase = PHASE_HASH; + mvHash = mv; + mvKiller1 = lpwmvKiller[0]; + mvKiller2 = lpwmvKiller[1]; + } + int InitEvade(PositionStruct &pos, int mv, const uint16_t *lpwmvKiller); + int NextFull(const PositionStruct &pos); + + // 根结点着法顺序控制 + void InitRoot(const PositionStruct &pos, int nBanMoves, const uint16_t *lpwmvBanList); + void ResetRoot(bool bUnique = false) { + nMoveIndex = 0; + ShellSort(); + nMoveIndex = (bUnique ? 1 : 0); + } + int NextRoot(void) { + if (nMoveIndex < nMoveNum) { + nMoveIndex ++; + return mvs[nMoveIndex - 1].wmv; + } else { + return 0; + } + } + void UpdateRoot(int mv); +}; + +// 清空历史表 +inline void ClearHistory(void) { + memset(nHistory, 0, sizeof(int[65536])); +} + +// 清空杀手着法表 +inline void ClearKiller(uint16_t (*lpwmvKiller)[2]) { + memset(lpwmvKiller, 0, LIMIT_DEPTH * sizeof(uint16_t[2])); +} + +// 复制杀手着法表 +inline void CopyKiller(uint16_t (*lpwmvDst)[2], const uint16_t (*lpwmvSrc)[2]) { + memcpy(lpwmvDst, lpwmvSrc, LIMIT_DEPTH * sizeof(uint16_t[2])); +} + +/* 找到最佳着法时采取的措施 + * + * 历史表的深度相关增量有以下几种选择: + * 1. 平方关系(n^2); + * 2. 指数关系(2^n); + * 3. Fibonacci数列; + * 4. 以上几种情况的组合,例如:n^2 + 2^n,等等。 + * ElephantEye使用最传统的平方关系。 + */ +inline void SetBestMove(int mv, int nDepth, uint16_t *lpwmvKiller) { + nHistory[mv] += SQR(nDepth); + if (lpwmvKiller[0] != mv) { + lpwmvKiller[1] = lpwmvKiller[0]; + lpwmvKiller[0] = mv; + } +} + +#endif diff --git a/eleeye/position.cpp b/eleeye/position.cpp new file mode 100644 index 0000000..7314ef9 --- /dev/null +++ b/eleeye/position.cpp @@ -0,0 +1,902 @@ +/* +position.h/position.cpp - Source Code for ElephantEye, Part III + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.0, Last Modified: Nov. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../base/base.h" +#include "../base/x86asm.h" +#include "pregen.h" +#include "position.h" + +/* ElephantEye源程序使用的匈牙利记号约定: + * + * sq: 格子序号(整数,从0到255,参阅"pregen.cpp") + * pc: 棋子序号(整数,从0到47,参阅"position.cpp") + * pt: 棋子类型序号(整数,从0到6,参阅"position.cpp") + * mv: 着法(整数,从0到65535,参阅"position.cpp") + * sd: 走子方(整数,0代表红方,1代表黑方) + * vl: 局面价值(整数,从"-MATE_VALUE"到"MATE_VALUE",参阅"position.cpp") + * (注:以上五个记号可与uc、dw等代表整数的记号配合使用) + * pos: 局面(PositionStruct类型,参阅"position.h") + * sms: 位行和位列的着法生成预置结构(参阅"pregen.h") + * smv: 位行和位列的着法判断预置结构(参阅"pregen.h") + */ + +// 本模块涉及多个"PositionStruct"的成员,用"this->"标记出来以方便辨认 + +// 起始局面的FEN串 +const char *const cszStartFen = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w"; + +// 棋子类型对应的棋子符号 +const char *const cszPieceBytes = "KABNRCP"; + +/* 棋子序号对应的棋子类型 + * + * ElephantEye的棋子序号从0到47,其中0到15不用,16到31表示红子,32到47表示黑子。 + * 每方的棋子顺序依次是:帅仕仕相相马马车车炮炮兵兵兵兵兵(将士士象象马马车车炮炮卒卒卒卒卒) + * 提示:判断棋子是红子用"pc < 32",黑子用"pc >= 32" + */ +const int cnPieceTypes[48] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 6, 6, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 6, 6 +}; + +// 棋子的简单分值,只在简单比较时作参考 +const int cnSimpleValues[48] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 1, 1, 1, 1, 3, 3, 4, 4, 3, 3, 2, 2, 2, 2, 2, + 5, 1, 1, 1, 1, 3, 3, 4, 4, 3, 3, 2, 2, 2, 2, 2, +}; + +// 该数组很方便地实现了坐标的镜像(左右对称) +const uint8_t cucsqMirrorTab[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0, 0, 0, 0, + 0, 0, 0, 0x4b, 0x4a, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0, 0, 0, 0, + 0, 0, 0, 0x5b, 0x5a, 0x59, 0x58, 0x57, 0x56, 0x55, 0x54, 0x53, 0, 0, 0, 0, + 0, 0, 0, 0x6b, 0x6a, 0x69, 0x68, 0x67, 0x66, 0x65, 0x64, 0x63, 0, 0, 0, 0, + 0, 0, 0, 0x7b, 0x7a, 0x79, 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0, 0, 0, 0, + 0, 0, 0, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0, 0, 0, 0, + 0, 0, 0, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0, 0, 0, 0, + 0, 0, 0, 0xab, 0xaa, 0xa9, 0xa8, 0xa7, 0xa6, 0xa5, 0xa4, 0xa3, 0, 0, 0, 0, + 0, 0, 0, 0xbb, 0xba, 0xb9, 0xb8, 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0, 0, 0, 0, + 0, 0, 0, 0xcb, 0xca, 0xc9, 0xc8, 0xc7, 0xc6, 0xc5, 0xc4, 0xc3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +// FEN串中棋子标识,注意这个函数只能识别大写字母,因此用小写字母时,首先必须转换为大写 +int FenPiece(int nArg) { + switch (nArg) { + case 'K': + return 0; + case 'A': + return 1; + case 'B': + case 'E': + return 2; + case 'N': + case 'H': + return 3; + case 'R': + return 4; + case 'C': + return 5; + case 'P': + return 6; + default: + return 7; + } +} + +// 以下是一些棋盘处理过程 + +// 棋盘上增加棋子 +void PositionStruct::AddPiece(int sq, int pc, bool bDel) { + int pt; + + __ASSERT_SQUARE(sq); + __ASSERT_PIECE(pc); + if (bDel) { + this->ucpcSquares[sq] = 0; + this->ucsqPieces[pc] = 0; + } else { + this->ucpcSquares[sq] = pc; + this->ucsqPieces[pc] = sq; + } + this->wBitRanks[RANK_Y(sq)] ^= PreGen.wBitRankMask[sq]; + this->wBitFiles[FILE_X(sq)] ^= PreGen.wBitFileMask[sq]; + __ASSERT_BITRANK(this->wBitRanks[RANK_Y(sq)]); + __ASSERT_BITFILE(this->wBitRanks[FILE_X(sq)]); + this->dwBitPiece ^= BIT_PIECE(pc); + pt = PIECE_TYPE(pc); + if (pc < 32) { + if (bDel) { + this->vlWhite -= PreEval.ucvlWhitePieces[pt][sq]; + } else { + this->vlWhite += PreEval.ucvlWhitePieces[pt][sq]; + } + } else { + if (bDel) { + this->vlBlack -= PreEval.ucvlBlackPieces[pt][sq]; + } else { + this->vlBlack += PreEval.ucvlBlackPieces[pt][sq]; + } + pt += 7; + } + __ASSERT_BOUND(0, pt, 13); + this->zobr.Xor(PreGen.zobrTable[pt][sq]); +} + +// 移动棋子 +int PositionStruct::MovePiece(int mv) { + int sqSrc, sqDst, pcMoved, pcCaptured, pt; + uint8_t *lpucvl; + // 移动棋子包括以下几个步骤: + + // 1. 得到移动的棋子序号和被吃的棋子序号; + sqSrc = SRC(mv); + sqDst = DST(mv); + pcMoved = this->ucpcSquares[sqSrc]; + __ASSERT_SQUARE(sqSrc); + __ASSERT_SQUARE(sqDst); + __ASSERT_PIECE(pcMoved); + pcCaptured = this->ucpcSquares[sqDst]; + if (pcCaptured == 0) { + + // 2. 如果没有被吃的棋子,那么更新目标格的位行和位列。 + // 换句话说,有被吃的棋子,目标格的位行和位列就不必更新了。 + this->wBitRanks[RANK_Y(sqDst)] ^= PreGen.wBitRankMask[sqDst]; + this->wBitFiles[FILE_X(sqDst)] ^= PreGen.wBitFileMask[sqDst]; + __ASSERT_BITRANK(this->wBitRanks[RANK_Y(sqDst)]); + __ASSERT_BITFILE(this->wBitRanks[FILE_X(sqDst)]); + } else { + + __ASSERT_PIECE(pcCaptured); + // 3. 如果有被吃的棋子,那么更新棋子位,从"ucsqPieces"数组中清除被吃棋子 + // 同时更新子力价值、位行位列、Zobrist键值和校验锁 + this->ucsqPieces[pcCaptured] = 0; + this->dwBitPiece ^= BIT_PIECE(pcCaptured); + pt = PIECE_TYPE(pcCaptured); + if (pcCaptured < 32) { + this->vlWhite -= PreEval.ucvlWhitePieces[pt][sqDst]; + } else { + this->vlBlack -= PreEval.ucvlBlackPieces[pt][sqDst]; + pt += 7; + } + __ASSERT_BOUND(0, pt, 13); + this->zobr.Xor(PreGen.zobrTable[pt][sqDst]); + } + + // 4. 从"ucpcSquares"和"ucsqPieces"数组中移动棋子,注意“格子-棋子联系数组”移动棋子的方法 + // 同时更新位行、位列、子力价值、位行位列、Zobrist键值和校验锁 + this->ucpcSquares[sqSrc] = 0; + this->ucpcSquares[sqDst] = pcMoved; + this->ucsqPieces[pcMoved] = sqDst; + this->wBitRanks[RANK_Y(sqSrc)] ^= PreGen.wBitRankMask[sqSrc]; + this->wBitFiles[FILE_X(sqSrc)] ^= PreGen.wBitFileMask[sqSrc]; + __ASSERT_BITRANK(this->wBitRanks[RANK_Y(sqSrc)]); + __ASSERT_BITFILE(this->wBitRanks[FILE_X(sqSrc)]); + pt = PIECE_TYPE(pcMoved); + if (pcMoved < 32) { + lpucvl = PreEval.ucvlWhitePieces[pt]; + this->vlWhite += lpucvl[sqDst] - lpucvl[sqSrc]; + } else { + lpucvl = PreEval.ucvlBlackPieces[pt]; + this->vlBlack += lpucvl[sqDst] - lpucvl[sqSrc]; + pt += 7; + } + __ASSERT_BOUND(0, pt, 13); + this->zobr.Xor(PreGen.zobrTable[pt][sqDst], PreGen.zobrTable[pt][sqSrc]); + return pcCaptured; +} + +// 撤消移动棋子 +void PositionStruct::UndoMovePiece(int mv, int pcCaptured) { + int sqSrc, sqDst, pcMoved; + sqSrc = SRC(mv); + sqDst = DST(mv); + pcMoved = this->ucpcSquares[sqDst]; + __ASSERT_SQUARE(sqSrc); + __ASSERT_SQUARE(sqDst); + __ASSERT_PIECE(pcMoved); + this->ucpcSquares[sqSrc] = pcMoved; + this->ucsqPieces[pcMoved] = sqSrc; + this->wBitRanks[RANK_Y(sqSrc)] ^= PreGen.wBitRankMask[sqSrc]; + this->wBitFiles[FILE_X(sqSrc)] ^= PreGen.wBitFileMask[sqSrc]; + __ASSERT_BITRANK(this->wBitRanks[RANK_Y(sqSrc)]); + __ASSERT_BITFILE(this->wBitRanks[FILE_X(sqSrc)]); + if (pcCaptured > 0) { + __ASSERT_PIECE(pcCaptured); + this->ucpcSquares[sqDst] = pcCaptured; + this->ucsqPieces[pcCaptured] = sqDst; + this->dwBitPiece ^= BIT_PIECE(pcCaptured); + } else { + this->ucpcSquares[sqDst] = 0; + this->wBitRanks[RANK_Y(sqDst)] ^= PreGen.wBitRankMask[sqDst]; + this->wBitFiles[FILE_X(sqDst)] ^= PreGen.wBitFileMask[sqDst]; + __ASSERT_BITRANK(this->wBitRanks[RANK_Y(sqDst)]); + __ASSERT_BITFILE(this->wBitRanks[FILE_X(sqDst)]); + } +} + +#include + +// 升变 +int PositionStruct::Promote(int sq) { + int pcCaptured, pcPromoted, pt; + // 升变包括以下几个步骤: + + // 1. 得到升变前棋子的序号; + __ASSERT_SQUARE(sq); + __ASSERT(CanPromote()); + __ASSERT(CAN_PROMOTE(sq)); + pcCaptured = this->ucpcSquares[sq]; + __ASSERT_PIECE(pcCaptured); + pcPromoted = SIDE_TAG(this->sdPlayer) + Bsf(~this->wBitPiece[this->sdPlayer] & PAWN_BITPIECE); + __ASSERT_PIECE(pcPromoted); + __ASSERT(this->ucsqPieces[pcPromoted] == 0); + + // 2. 去掉升变前棋子,同时更新子力价值、Zobrist键值和校验锁 + this->dwBitPiece ^= BIT_PIECE(pcPromoted) ^ BIT_PIECE(pcCaptured); + this->ucsqPieces[pcCaptured] = 0; + pt = PIECE_TYPE(pcCaptured); + if (pcCaptured < 32) { + this->vlWhite -= PreEval.ucvlWhitePieces[pt][sq]; + } else { + this->vlBlack -= PreEval.ucvlBlackPieces[pt][sq]; + pt += 7; + } + __ASSERT_BOUND(0, pt, 13); + this->zobr.Xor(PreGen.zobrTable[pt][sq]); + + // 3. 加上升变后棋子,同时更新子力价值、Zobrist键值和校验锁 + this->ucpcSquares[sq] = pcPromoted; + this->ucsqPieces[pcPromoted] = sq; + pt = PIECE_TYPE(pcPromoted); + if (pcPromoted < 32) { + this->vlWhite += PreEval.ucvlWhitePieces[pt][sq]; + } else { + this->vlBlack += PreEval.ucvlBlackPieces[pt][sq]; + pt += 7; + } + __ASSERT_BOUND(0, pt, 13); + this->zobr.Xor(PreGen.zobrTable[pt][sq]); + return pcCaptured; +} + +// 撤消升变 +void PositionStruct::UndoPromote(int sq, int pcCaptured) { + int pcPromoted; + __ASSERT_SQUARE(sq); + __ASSERT_PIECE(pcCaptured); + pcPromoted = this->ucpcSquares[sq]; + __ASSERT(PIECE_TYPE(pcPromoted) == 6); + this->ucsqPieces[pcPromoted] = 0; + this->ucpcSquares[sq] = pcCaptured; + this->ucsqPieces[pcCaptured] = sq; + this->dwBitPiece ^= BIT_PIECE(pcPromoted) ^ BIT_PIECE(pcCaptured); +} + +// 以上是一些棋盘处理过程 + +// 以下是一些着法处理过程 + +// 执行一个着法 +bool PositionStruct::MakeMove(int mv) { + int sq, pcCaptured; + uint32_t dwOldZobristKey; + RollbackStruct *lprbs; + + // 如果达到最大着法数,那么判定为非法着法 + if (this->nMoveNum == MAX_MOVE_NUM) { + return false; + } + __ASSERT(this->nMoveNum < MAX_MOVE_NUM); + // 执行一个着法要包括以下几个步骤: + + // 1. 保存原来的Zobrist键值 + dwOldZobristKey = this->zobr.dwKey; + SaveStatus(); + + // 2. 移动棋子,记住吃掉的子(如果有的话) + sq = SRC(mv); + if (sq == DST(mv)) { + pcCaptured = Promote(sq); + } else { + pcCaptured = MovePiece(mv); + + // 3. 如果移动后被将军了,那么着法是非法的,撤消该着法 + if (CheckedBy(CHECK_LAZY) > 0) { + UndoMovePiece(mv, pcCaptured); + Rollback(); + return false; + } + } + + // 4. 交换走子方 + ChangeSide(); + + // 5. 把原来的Zobrist键值记录到检测重复的迷你置换表中 + if (this->ucRepHash[dwOldZobristKey & REP_HASH_MASK] == 0) { + this->ucRepHash[dwOldZobristKey & REP_HASH_MASK] = this->nMoveNum; + } + + // 6. 把着法保存到历史着法列表中,并记住吃掉的子和将军状态 + lprbs = this->rbsList + this->nMoveNum; + lprbs->mvs.wmv = mv; + lprbs->mvs.ChkChs = CheckedBy(); + + // 7. 设置和棋着法数(将军和应将不计入) + if (pcCaptured == 0) { + if (lprbs->mvs.ChkChs == 0) { + lprbs->mvs.ChkChs = -ChasedBy(mv); + } + if (LastMove().CptDrw == -100) { + lprbs->mvs.CptDrw = -100; + } else { + lprbs->mvs.CptDrw = MIN((int) LastMove().CptDrw, 0) - (lprbs->mvs.ChkChs > 0 || LastMove().ChkChs > 0 ? 0 : 1); + } + __ASSERT_BOUND(-100, lprbs->mvs.CptDrw, 0); + } else { + lprbs->mvs.CptDrw = pcCaptured; + __ASSERT_PIECE(pcCaptured); + } + this->nMoveNum ++; + this->nDistance ++; + + return true; +} + +// 撤消一个着法 +void PositionStruct::UndoMakeMove(void) { + int sq; + RollbackStruct *lprbs; + this->nMoveNum --; + this->nDistance --; + lprbs = this->rbsList + this->nMoveNum; + sq = SRC(lprbs->mvs.wmv); + if (sq == DST(lprbs->mvs.wmv)) { + __ASSERT_BOUND(ADVISOR_TYPE, PIECE_TYPE(lprbs->mvs.CptDrw), BISHOP_TYPE); + UndoPromote(sq, lprbs->mvs.CptDrw); + } else { + UndoMovePiece(lprbs->mvs.wmv, lprbs->mvs.CptDrw); + } + this->sdPlayer = OPP_SIDE(this->sdPlayer); + Rollback(); + if (this->ucRepHash[this->zobr.dwKey & REP_HASH_MASK] == this->nMoveNum) { + this->ucRepHash[this->zobr.dwKey & REP_HASH_MASK] = 0; + } + __ASSERT(this->nMoveNum > 0); +} + +// 执行一个空着 +void PositionStruct::NullMove(void) { + __ASSERT(this->nMoveNum < MAX_MOVE_NUM); + if (this->ucRepHash[this->zobr.dwKey & REP_HASH_MASK] == 0) { + this->ucRepHash[this->zobr.dwKey & REP_HASH_MASK] = this->nMoveNum; + } + SaveStatus(); + ChangeSide(); + this->rbsList[nMoveNum].mvs.dwmv = 0; // wmv, Chk, CptDrw, ChkChs = 0 + this->nMoveNum ++; + this->nDistance ++; +} + +// 撤消一个空着 +void PositionStruct::UndoNullMove(void) { + this->nMoveNum --; + this->nDistance --; + this->sdPlayer = OPP_SIDE(this->sdPlayer); + Rollback(); + if (this->ucRepHash[this->zobr.dwKey & REP_HASH_MASK] == this->nMoveNum) { + this->ucRepHash[this->zobr.dwKey & REP_HASH_MASK] = 0; + } + __ASSERT(this->nMoveNum > 0); +} + +// 以上是一些着法处理过程 + +// 以下是一些局面处理过程 + +// FEN串识别 +void PositionStruct::FromFen(const char *szFen) { + int i, j, k; + int pcWhite[7]; + int pcBlack[7]; + const char *lpFen; + // FEN串的识别包括以下几个步骤: + // 1. 初始化,清空棋盘 + pcWhite[0] = SIDE_TAG(0) + KING_FROM; + pcWhite[1] = SIDE_TAG(0) + ADVISOR_FROM; + pcWhite[2] = SIDE_TAG(0) + BISHOP_FROM; + pcWhite[3] = SIDE_TAG(0) + KNIGHT_FROM; + pcWhite[4] = SIDE_TAG(0) + ROOK_FROM; + pcWhite[5] = SIDE_TAG(0) + CANNON_FROM; + pcWhite[6] = SIDE_TAG(0) + PAWN_FROM; + for (i = 0; i < 7; i ++) { + pcBlack[i] = pcWhite[i] + 16; + } + /* 数组"pcWhite[7]"和"pcBlack[7]"分别代表红方和黑方每个兵种即将占有的序号, + * 以"pcWhite[7]"为例,由于棋子16到31依次代表“帅仕仕相相马马车车炮炮兵兵兵兵兵”, + * 所以最初应该是"pcWhite[7] = {16, 17, 19, 21, 23, 25, 27}",每添加一个棋子,该项就增加1, + * 这种做法允许添加多余的棋子(例如添加第二个帅,就变成仕了),但添加前要做边界检测 + */ + ClearBoard(); + lpFen = szFen; + if (*lpFen == '\0') { + SetIrrev(); + return; + } + // 2. 读取棋盘上的棋子 + i = RANK_TOP; + j = FILE_LEFT; + while (*lpFen != ' ') { + if (*lpFen == '/') { + j = FILE_LEFT; + i ++; + if (i > RANK_BOTTOM) { + break; + } + } else if (*lpFen >= '1' && *lpFen <= '9') { + for (k = 0; k < (*lpFen - '0'); k ++) { + if (j >= FILE_RIGHT) { + break; + } + j ++; + } + } else if (*lpFen >= 'A' && *lpFen <= 'Z') { + if (j <= FILE_RIGHT) { + k = FenPiece(*lpFen); + if (k < 7) { + if (pcWhite[k] < 32) { + if (this->ucsqPieces[pcWhite[k]] == 0) { + AddPiece(COORD_XY(j, i), pcWhite[k]); + pcWhite[k] ++; + } + } + } + j ++; + } + } else if (*lpFen >= 'a' && *lpFen <= 'z') { + if (j <= FILE_RIGHT) { + k = FenPiece(*lpFen + 'A' - 'a'); + if (k < 7) { + if (pcBlack[k] < 48) { + if (this->ucsqPieces[pcBlack[k]] == 0) { + AddPiece(COORD_XY(j, i), pcBlack[k]); + pcBlack[k] ++; + } + } + } + j ++; + } + } + lpFen ++; + if (*lpFen == '\0') { + SetIrrev(); + return; + } + } + lpFen ++; + // 3. 确定轮到哪方走 + if (this->sdPlayer == (*lpFen == 'b' ? 0 : 1)) { + ChangeSide(); + } + // 4. 把局面设成“不可逆” + SetIrrev(); +} + +// 生成FEN串 +void PositionStruct::ToFen(char *szFen) const { + int i, j, k, pc; + char *lpFen; + + lpFen = szFen; + for (i = RANK_TOP; i <= RANK_BOTTOM; i ++) { + k = 0; + for (j = FILE_LEFT; j <= FILE_RIGHT; j ++) { + pc = this->ucpcSquares[COORD_XY(j, i)]; + if (pc != 0) { + if (k > 0) { + *lpFen = k + '0'; + lpFen ++; + k = 0; + } + *lpFen = PIECE_BYTE(PIECE_TYPE(pc)) + (pc < 32 ? 0 : 'a' - 'A'); + lpFen ++; + } else { + k ++; + } + } + if (k > 0) { + *lpFen = k + '0'; + lpFen ++; + } + *lpFen = '/'; + lpFen ++; + } + *(lpFen - 1) = ' '; // 把最后一个'/'替换成' ' + *lpFen = (this->sdPlayer == 0 ? 'w' : 'b'); + lpFen ++; + *lpFen = '\0'; +} + +// 局面镜像 +void PositionStruct::Mirror(void) { + int i, sq, nMoveNumSave; + uint16_t wmvList[MAX_MOVE_NUM]; + uint8_t ucsqList[32]; + // 局面镜像需要按以下步骤依次进行: + + // 1. 记录所有历史着法 + nMoveNumSave = this->nMoveNum; + for (i = 1; i < nMoveNumSave; i ++) { + wmvList[i] = this->rbsList[i].mvs.wmv; + } + + // 2. 撤消所有着法 + for (i = 1; i < nMoveNumSave; i ++) { + UndoMakeMove(); + } + + // 3. 把所有棋子从棋盘上拿走,位置记录到"ucsqList"数组; + for (i = 16; i < 48; i ++) { + sq = this->ucsqPieces[i]; + ucsqList[i - 16] = sq; + if (sq != 0) { + AddPiece(sq, i, DEL_PIECE); + } + } + + // 4. 把拿走的棋子按照镜像位置重新放到棋盘上; + for (i = 16; i < 48; i ++) { + sq = ucsqList[i - 16]; + if (sq != 0) { + AddPiece(SQUARE_MIRROR(sq), i); + } + } + + // 6. 还原镜像着法 + SetIrrev(); + for (i = 1; i < nMoveNumSave; i ++) { + MakeMove(MOVE_MIRROR(wmvList[i])); + } +} + +// 以上是一些局面处理过程 + +// 以下是一些着法检测过程 + +// 着法合理性检测,仅用在“杀手着法”的检测中 +bool PositionStruct::LegalMove(int mv) const { + int sqSrc, sqDst, sqPin, pcMoved, pcCaptured, x, y, nSideTag; + // 着法合理性检测包括以下几个步骤: + + // 1. 检查要走的子是否存在 + nSideTag = SIDE_TAG(this->sdPlayer); + sqSrc = SRC(mv); + sqDst = DST(mv); + pcMoved = this->ucpcSquares[sqSrc]; + if ((pcMoved & nSideTag) == 0) { + return false; + } + __ASSERT_SQUARE(sqSrc); + __ASSERT_SQUARE(sqDst); + __ASSERT_PIECE(pcMoved); + + // 2. 检查吃到的子是否为对方棋子(如果有吃子并且没有升变的话) + pcCaptured = this->ucpcSquares[sqDst]; + if (sqSrc != sqDst && (pcCaptured & nSideTag) != 0) { + return false; + } + __ASSERT_BOUND(0, PIECE_INDEX(pcMoved), 15); + switch (PIECE_INDEX(pcMoved)) { + + // 3. 如果是帅(将)或仕(士),则先看是否在九宫内,再看是否是合理位移 + case KING_FROM: + return IN_FORT(sqDst) && KING_SPAN(sqSrc, sqDst); + case ADVISOR_FROM: + case ADVISOR_TO: + if (sqSrc == sqDst) { + // 考虑升变,在底线并且兵(卒)不全时,才可升变 + return CAN_PROMOTE(sqSrc) && CanPromote(); + } else { + return IN_FORT(sqDst) && ADVISOR_SPAN(sqSrc, sqDst); + } + + // 4. 如果是相(象),则先看是否过河,再看是否是合理位移,最后看有没有被塞象眼 + case BISHOP_FROM: + case BISHOP_TO: + if (sqSrc == sqDst) { + // 考虑升变,在底线并且兵(卒)不全时,才可升变 + return CAN_PROMOTE(sqSrc) && CanPromote(); + } else { + return SAME_HALF(sqSrc, sqDst) && BISHOP_SPAN(sqSrc, sqDst) && this->ucpcSquares[BISHOP_PIN(sqSrc, sqDst)] == 0; + } + + // 5. 如果是马,则先看看是否是合理位移,再看有没有被蹩马腿 + case KNIGHT_FROM: + case KNIGHT_TO: + sqPin = KNIGHT_PIN(sqSrc, sqDst); + return sqPin != sqSrc && this->ucpcSquares[sqPin] == 0; + + // 6. 如果是车,则先看是横向移动还是纵向移动,再读取位行或位列的着法预生成数组 + case ROOK_FROM: + case ROOK_TO: + x = FILE_X(sqSrc); + y = RANK_Y(sqSrc); + if (x == FILE_X(sqDst)) { + if (pcCaptured == 0) { + return (FileMaskPtr(x, y)->wNonCap & PreGen.wBitFileMask[sqDst]) != 0; + } else { + return (FileMaskPtr(x, y)->wRookCap & PreGen.wBitFileMask[sqDst]) != 0; + } + } else if (y == RANK_Y(sqDst)) { + if (pcCaptured == 0) { + return (RankMaskPtr(x, y)->wNonCap & PreGen.wBitRankMask[sqDst]) != 0; + } else { + return (RankMaskPtr(x, y)->wRookCap & PreGen.wBitRankMask[sqDst]) != 0; + } + } else { + return false; + } + + // 7. 如果是炮,判断起来和车一样 + case CANNON_FROM: + case CANNON_TO: + x = FILE_X(sqSrc); + y = RANK_Y(sqSrc); + if (x == FILE_X(sqDst)) { + if (pcCaptured == 0) { + return (FileMaskPtr(x, y)->wNonCap & PreGen.wBitFileMask[sqDst]) != 0; + } else { + return (FileMaskPtr(x, y)->wCannonCap & PreGen.wBitFileMask[sqDst]) != 0; + } + } else if (y == RANK_Y(sqDst)) { + if (pcCaptured == 0) { + return (RankMaskPtr(x, y)->wNonCap & PreGen.wBitRankMask[sqDst]) != 0; + } else { + return (RankMaskPtr(x, y)->wCannonCap & PreGen.wBitRankMask[sqDst]) != 0; + } + } else { + return false; + } + + // 8. 如果是兵(卒),则按红方和黑方分情况讨论 + default: + if (AWAY_HALF(sqDst, this->sdPlayer) && (sqDst == sqSrc - 1 || sqDst == sqSrc + 1)) { + return true; + } else { + return sqDst == SQUARE_FORWARD(sqSrc, this->sdPlayer); + } + } +} + +// 将军检测 +int PositionStruct::CheckedBy(bool bLazy) const { + int pcCheckedBy, i, sqSrc, sqDst, sqPin, pc, x, y, nOppSideTag; + SlideMaskStruct *lpsmsRank, *lpsmsFile; + + pcCheckedBy = 0; + nOppSideTag = OPP_SIDE_TAG(this->sdPlayer); + // 将军判断包括以下几部分内容: + + // 1. 判断帅(将)是否在棋盘上 + sqSrc = this->ucsqPieces[SIDE_TAG(this->sdPlayer)]; + if (sqSrc == 0) { + return 0; + } + __ASSERT_SQUARE(sqSrc); + + // 2. 获得帅(将)所在格子的位行和位列 + x = FILE_X(sqSrc); + y = RANK_Y(sqSrc); + lpsmsRank = RankMaskPtr(x, y); + lpsmsFile = FileMaskPtr(x, y); + + // 3. 判断是否将帅对脸 + sqDst = this->ucsqPieces[nOppSideTag + KING_FROM]; + if (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + if (x == FILE_X(sqDst) && (lpsmsFile->wRookCap & PreGen.wBitFileMask[sqDst]) != 0) { + return CHECK_MULTI; + } + } + + // 4. 判断是否被马将军 + for (i = KNIGHT_FROM; i <= KNIGHT_TO; i ++) { + sqDst = this->ucsqPieces[nOppSideTag + i]; + if (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + sqPin = KNIGHT_PIN(sqDst, sqSrc); // 注意,sqSrc和sqDst是反的! + if (sqPin != sqDst && this->ucpcSquares[sqPin] == 0) { + if (bLazy || pcCheckedBy > 0) { + return CHECK_MULTI; + } + pcCheckedBy = nOppSideTag + i; + __ASSERT_PIECE(pcCheckedBy); + } + } + } + + // 5. 判断是否被车将军或将帅对脸 + for (i = ROOK_FROM; i <= ROOK_TO; i ++) { + sqDst = this->ucsqPieces[nOppSideTag + i]; + if (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + if (x == FILE_X(sqDst)) { + if ((lpsmsFile->wRookCap & PreGen.wBitFileMask[sqDst]) != 0) { + if (bLazy || pcCheckedBy > 0) { + return CHECK_MULTI; + } + pcCheckedBy = nOppSideTag + i; + __ASSERT_PIECE(pcCheckedBy); + } + } else if (y == RANK_Y(sqDst)) { + if ((lpsmsRank->wRookCap & PreGen.wBitRankMask[sqDst]) != 0) { + if (bLazy || pcCheckedBy > 0) { + return CHECK_MULTI; + } + pcCheckedBy = nOppSideTag + i; + __ASSERT_PIECE(pcCheckedBy); + } + } + } + } + + // 6. 判断是否被炮将军 + for (i = CANNON_FROM; i <= CANNON_TO; i ++) { + sqDst = this->ucsqPieces[nOppSideTag + i]; + if (sqDst != 0) { + __ASSERT_SQUARE(sqDst); + if (x == FILE_X(sqDst)) { + if ((lpsmsFile->wCannonCap & PreGen.wBitFileMask[sqDst]) != 0) { + if (bLazy || pcCheckedBy > 0) { + return CHECK_MULTI; + } + pcCheckedBy = nOppSideTag + i; + __ASSERT_PIECE(pcCheckedBy); + } + } else if (y == RANK_Y(sqDst)) { + if ((lpsmsRank->wCannonCap & PreGen.wBitRankMask[sqDst]) != 0) { + if (bLazy || pcCheckedBy > 0) { + return CHECK_MULTI; + } + pcCheckedBy = nOppSideTag + i; + __ASSERT_PIECE(pcCheckedBy); + } + } + } + } + + // 7. 判断是否被兵(卒)将军 + for (sqDst = sqSrc - 1; sqDst <= sqSrc + 1; sqDst += 2) { + // 如果帅(将)在边线(ElephantEye允许),那么断言不成立 + // __ASSERT_SQUARE(sqDst); + pc = this->ucpcSquares[sqDst]; + if ((pc & nOppSideTag) != 0 && PIECE_INDEX(pc) >= PAWN_FROM) { + if (bLazy || pcCheckedBy > 0) { + return CHECK_MULTI; + } + pcCheckedBy = nOppSideTag + i; + __ASSERT_PIECE(pcCheckedBy); + } + } + pc = this->ucpcSquares[SQUARE_FORWARD(sqSrc, this->sdPlayer)]; + if ((pc & nOppSideTag) != 0 && PIECE_INDEX(pc) >= PAWN_FROM) { + if (bLazy || pcCheckedBy > 0) { + return CHECK_MULTI; + } + pcCheckedBy = nOppSideTag + i; + __ASSERT_PIECE(pcCheckedBy); + } + return pcCheckedBy; +} + +// 判断是否被将死 +bool PositionStruct::IsMate(void) { + int i, nGenNum; + MoveStruct mvsGen[MAX_GEN_MOVES]; + nGenNum = GenCapMoves(mvsGen); + for (i = 0; i < nGenNum; i ++) { + if (MakeMove(mvsGen[i].wmv)) { + UndoMakeMove(); + return false; + } + } + // 着法生成分两部分做,这样可以节约时间 + nGenNum = GenNonCapMoves(mvsGen); + for (i = 0; i < nGenNum; i ++) { + if (MakeMove(mvsGen[i].wmv)) { + UndoMakeMove(); + return false; + } + } + return true; +} + +// 设置将军状态位 +inline void SetPerpCheck(uint32_t &dwPerpCheck, int nChkChs) { + if (nChkChs == 0) { + dwPerpCheck = 0; + } else if (nChkChs > 0) { + dwPerpCheck &= 0x10000; + } else { + dwPerpCheck &= (1 << -nChkChs); + } +} + +// 重复局面检测 +int PositionStruct::RepStatus(int nRecur) const { + // 参数"nRecur"指重复次数,在搜索中取1以提高搜索效率(默认值),根结点处取3以适应规则 + int sd; + uint32_t dwPerpCheck, dwOppPerpCheck; + const RollbackStruct *lprbs; + /* 重复局面检测包括以下几个步骤: + * + * 1. 首先判断检测重复的迷你置换表中是否可能有当前局面,如果没有可能,就用不着判断了 + * 置换表"ucRepHash"是ElephantEye的一个特色,局面每执行一个着法时,就会在置换表项中记录下当前的"nMoveNum" + * 如果置换表项已经填有其他局面,就不必覆盖了,参阅"MakeMove()"函数 + * 因此撤消着法时,只要查找置换表项的值是否等于当前的"nMoveNum",如果相等则清空该项 + * 如果不等于当前的"nMoveNum",则说明之前还有局面占有这个置换表项,不必清空该项,参阅"position.h"中的"UndoMakeMove()"函数 + */ + if (this->ucRepHash[this->zobr.dwKey & REP_HASH_MASK] == 0) { + return 0; + } + + // 2. 初始化 + sd = OPP_SIDE(this->sdPlayer); + dwPerpCheck = dwOppPerpCheck = 0x1ffff; + lprbs = this->rbsList + this->nMoveNum - 1; + + // 3. 检查上一个着法,如果是空着或吃子着法,就不可能有重复了 + while (lprbs->mvs.wmv != 0 && lprbs->mvs.CptDrw <= 0) { + __ASSERT(lprbs >= this->rbsList); + + // 4. 判断双方的长打级别,0表示无长打,0xffff表示长捉,0x10000表示长将 + if (sd == this->sdPlayer) { + SetPerpCheck(dwPerpCheck, lprbs->mvs.ChkChs); + + // 5. 寻找重复局面,如果重复次数达到预定次数,则返回重复记号 + if (lprbs->zobr.dwLock0 == this->zobr.dwLock0 && lprbs->zobr.dwLock1 == this->zobr.dwLock1) { + nRecur --; + if (nRecur == 0) { + dwPerpCheck = ((dwPerpCheck & 0xffff) == 0 ? dwPerpCheck : 0xffff); + dwOppPerpCheck = ((dwOppPerpCheck & 0xffff) == 0 ? dwOppPerpCheck : 0xffff); + return dwPerpCheck > dwOppPerpCheck ? REP_LOSS : dwPerpCheck < dwOppPerpCheck ? REP_WIN : REP_DRAW; + } + } + + } else { + SetPerpCheck(dwOppPerpCheck, lprbs->mvs.ChkChs); + } + + sd = OPP_SIDE(sd); + lprbs --; + } + return REP_NONE; +} + +// 以上是一些着法检测过程 diff --git a/eleeye/position.h b/eleeye/position.h new file mode 100644 index 0000000..597d41b --- /dev/null +++ b/eleeye/position.h @@ -0,0 +1,383 @@ +/* +position.h/position.cpp - Source Code for ElephantEye, Part III + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.12, Last Modified: Dec. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "../base/base.h" +#include "pregen.h" + +/* ElephantEye源程序使用的匈牙利记号约定: + * + * sq: 格子序号(整数,从0到255,参阅"pregen.cpp") + * pc: 棋子序号(整数,从0到47,参阅"position.cpp") + * pt: 棋子类型序号(整数,从0到6,参阅"position.cpp") + * mv: 着法(整数,从0到65535,参阅"position.cpp") + * sd: 走子方(整数,0代表红方,1代表黑方) + * vl: 局面价值(整数,从"-MATE_VALUE"到"MATE_VALUE",参阅"position.cpp") + * (注:以上四个记号可与uc、dw等代表整数的记号配合使用) + * pos: 局面(PositionStruct类型,参阅"position.h") + * sms: 位行和位列的着法生成预置结构(参阅"pregen.h") + * smv: 位行和位列的着法判断预置结构(参阅"pregen.h") + */ + +#ifndef POSITION_H +#define POSITION_H + +const int MAX_MOVE_NUM = 256; // 局面能容纳的回滚着法数 +const int MAX_GEN_MOVES = 128; // 搜索的最大着法数,中国象棋的任何局面都不会超过120个着法 +const int DRAW_MOVES = 100; // 默认的和棋着法数,ElephantEye设定在50回合即100步,但将军和应将不计入其中 +const int REP_HASH_MASK = 1023; // 判断重复局面的迷你置换表长度,即1024个表项 + +const int MATE_VALUE = 10000; // 最高分值,即将死的分值 +const int BAN_VALUE = MATE_VALUE - 100; // 长将判负的分值,低于该值将不写入置换表(参阅"hash.cpp") +const int WIN_VALUE = MATE_VALUE - 200; // 搜索出胜负的分值界限,超出此值就说明已经搜索出杀棋了 +const int NULLOKAY_MARGIN = 200; // 空着裁剪可以不检验的子力价值边界 +const int NULLSAFE_MARGIN = 400; // 允许使用空着裁剪的条件的子力价值边界 +const int DRAW_VALUE = 20; // 和棋时返回的分数(取负值) + +const bool CHECK_LAZY = true; // 偷懒检测将军 +const int CHECK_MULTI = 48; // 被多个子将军 + +// 每种子力的类型编号 +const int KING_TYPE = 0; +const int ADVISOR_TYPE = 1; +const int BISHOP_TYPE = 2; +const int KNIGHT_TYPE = 3; +const int ROOK_TYPE = 4; +const int CANNON_TYPE = 5; +const int PAWN_TYPE = 6; + +// 每种子力的开始序号和结束序号 +const int KING_FROM = 0; +const int ADVISOR_FROM = 1; +const int ADVISOR_TO = 2; +const int BISHOP_FROM = 3; +const int BISHOP_TO = 4; +const int KNIGHT_FROM = 5; +const int KNIGHT_TO = 6; +const int ROOK_FROM = 7; +const int ROOK_TO = 8; +const int CANNON_FROM = 9; +const int CANNON_TO = 10; +const int PAWN_FROM = 11; +const int PAWN_TO = 15; + +// 各种子力的屏蔽位 +const int KING_BITPIECE = 1 << KING_FROM; +const int ADVISOR_BITPIECE = (1 << ADVISOR_FROM) | (1 << ADVISOR_TO); +const int BISHOP_BITPIECE = (1 << BISHOP_FROM) | (1 << BISHOP_TO); +const int KNIGHT_BITPIECE = (1 << KNIGHT_FROM) | (1 << KNIGHT_TO); +const int ROOK_BITPIECE = (1 << ROOK_FROM) | (1 << ROOK_TO); +const int CANNON_BITPIECE = (1 << CANNON_FROM) | (1 << CANNON_TO); +const int PAWN_BITPIECE = (1 << PAWN_FROM) | (1 << (PAWN_FROM + 1)) | + (1 << (PAWN_FROM + 2)) | (1 << (PAWN_FROM + 3)) | (1 << PAWN_TO); +const int ATTACK_BITPIECE = KNIGHT_BITPIECE | ROOK_BITPIECE | CANNON_BITPIECE | PAWN_BITPIECE; + +inline uint32_t BIT_PIECE(int pc) { + return 1 << (pc - 16); +} + +inline uint32_t WHITE_BITPIECE(int nBitPiece) { + return nBitPiece; +} + +inline uint32_t BLACK_BITPIECE(int nBitPiece) { + return nBitPiece << 16; +} + +inline uint32_t BOTH_BITPIECE(int nBitPiece) { + return nBitPiece + (nBitPiece << 16); +} + +// "RepStatus()"返回的重复标记位 +const int REP_NONE = 0; +const int REP_DRAW = 1; +const int REP_LOSS = 3; +const int REP_WIN = 5; + +/* ElephantEye的很多代码中都用到"SIDE_TAG()"这个量,红方设为16,黑方设为32。 + * 用"SIDE_TAG() + i"可以方便地选择棋子的类型,"i"从0到15依次是: + * 帅仕仕相相马马车车炮炮兵兵兵兵兵(将士士象象马马车车炮炮卒卒卒卒卒) + * 例如"i"取"KNIGHT_FROM"到"KNIGHT_TO",则表示依次检查两个马的位置 + */ +inline int SIDE_TAG(int sd) { + int pc = 16 + (sd << 4); + return pc; +} + +inline int OPP_SIDE_TAG(int sd) { + int pc = 32 - (sd << 4); + return pc; +} + +inline int SIDE_VALUE(int sd, int vl) { + return (sd == 0 ? vl : -vl); +} + +inline int PIECE_INDEX(int pc) { + return pc & 15; +} + +extern const char *const cszStartFen; // 起始局面的FEN串 +extern const char *const cszPieceBytes; // 棋子类型对应的棋子符号 +extern const int cnPieceTypes[48]; // 棋子序号对应的棋子类型 +extern const int cnSimpleValues[48]; // 棋子的简单分值 +extern const uint8_t cucsqMirrorTab[256]; // 坐标的镜像(左右对称)数组 + +inline char PIECE_BYTE(int pt) { + return cszPieceBytes[pt]; +} + +inline int PIECE_TYPE(int pc) { + return cnPieceTypes[pc]; +} + +inline int SIMPLE_VALUE(int pc) { + return cnSimpleValues[pc]; +} + +inline uint8_t SQUARE_MIRROR(int sq) { + return cucsqMirrorTab[sq]; +} + +// FEN串中棋子标识 +int FenPiece(int Arg); + +// 复杂着法结构 +union MoveStruct { + uint32_t dwmv; // 填满整个结构用 + struct { + uint16_t wmv, wvl; // 着法和分值 + }; + struct { + uint8_t Src, Dst; // 起始格和目标格 + int8_t CptDrw, ChkChs; // 被吃子(+)/和棋着法数(-)、将军子(+)/被捉子(-) + }; +}; // mvs + +// 着法结构 +inline int SRC(int mv) { // 得到着法的起点 + int sq = mv & 255; + return sq; +} + +inline int DST(int mv) { // 得到着法的终点 + int sq = mv >> 8; + return sq; +} + +inline int MOVE(int sqSrc, int sqDst) { // 由起点和终点得到着法 + return sqSrc + (sqDst << 8); +} + +inline uint32_t MOVE_COORD(int mv) { // 把着法转换成字符串 + union { + char c[4]; + uint32_t dw; + } Ret; + Ret.c[0] = FILE_X(SRC(mv)) - FILE_LEFT + 'a'; + Ret.c[1] = '9' - RANK_Y(SRC(mv)) + RANK_TOP; + Ret.c[2] = FILE_X(DST(mv)) - FILE_LEFT + 'a'; + Ret.c[3] = '9' - RANK_Y(DST(mv)) + RANK_TOP; + // 断言输出着法的合理性 + __ASSERT_BOUND('a', Ret.c[0], 'i'); + __ASSERT_BOUND('0', Ret.c[1], '9'); + __ASSERT_BOUND('a', Ret.c[2], 'i'); + __ASSERT_BOUND('0', Ret.c[3], '9'); + return Ret.dw; +} + +inline int COORD_MOVE(uint32_t dwMoveStr) { // 把字符串转换成着法 + int sqSrc, sqDst; + char *lpArgPtr; + lpArgPtr = (char *) &dwMoveStr; + sqSrc = COORD_XY(lpArgPtr[0] - 'a' + FILE_LEFT, '9' - lpArgPtr[1] + RANK_TOP); + sqDst = COORD_XY(lpArgPtr[2] - 'a' + FILE_LEFT, '9' - lpArgPtr[3] + RANK_TOP); + // 对输入着法的合理性不作断言 + // __ASSERT_SQUARE(sqSrc); + // __ASSERT_SQUARE(sqDst); + return (IN_BOARD(sqSrc) && IN_BOARD(sqDst) ? MOVE(sqSrc, sqDst) : 0); +} + +inline int MOVE_MIRROR(int mv) { // 对着法做镜像 + return MOVE(SQUARE_MIRROR(SRC(mv)), SQUARE_MIRROR(DST(mv))); +} + +// 回滚结构 +struct RollbackStruct { + ZobristStruct zobr; // Zobrist + int vlWhite, vlBlack; // 红方和黑方的子力价值 + MoveStruct mvs; // 着法 +}; // rbs + +const bool DEL_PIECE = true; // 函数"PositionStruct::AddPiece()"的选项 + +// 局面结构 +struct PositionStruct { + // 基本成员 + int sdPlayer; // 轮到哪方走,0表示红方,1表示黑方 + uint8_t ucpcSquares[256]; // 每个格子放的棋子,0表示没有棋子 + uint8_t ucsqPieces[48]; // 每个棋子放的位置,0表示被吃 + ZobristStruct zobr; // Zobrist + + // 位结构成员,用来增强棋盘的处理 + union { + uint32_t dwBitPiece; // 32位的棋子位,0到31位依次表示序号为16到47的棋子是否还在棋盘上 + uint16_t wBitPiece[2]; // 拆分成两个 + }; + uint16_t wBitRanks[16]; // 位行数组,注意用法是"wBitRanks[RANK_Y(sq)]" + uint16_t wBitFiles[16]; // 位列数组,注意用法是"wBitFiles[FILE_X(sq)]" + + // 局面评价数据 + int vlWhite, vlBlack; // 红方和黑方的子力价值 + + // 回滚着法,用来检测循环局面 + int nMoveNum, nDistance; // 回滚着法数和搜索深度 + RollbackStruct rbsList[MAX_MOVE_NUM]; // 回滚列表 + uint8_t ucRepHash[REP_HASH_MASK + 1]; // 判断重复局面的迷你置换表 + + // 获取着法预生成信息 + SlideMoveStruct *RankMovePtr(int x, int y) const { + return PreGen.smvRankMoveTab[x - FILE_LEFT] + wBitRanks[y]; + } + SlideMoveStruct *FileMovePtr(int x, int y) const { + return PreGen.smvFileMoveTab[y - RANK_TOP] + wBitFiles[x]; + } + SlideMaskStruct *RankMaskPtr(int x, int y) const { + return PreGen.smsRankMaskTab[x - FILE_LEFT] + wBitRanks[y]; + } + SlideMaskStruct *FileMaskPtr(int x, int y) const { + return PreGen.smsFileMaskTab[y - RANK_TOP] + wBitFiles[x]; + } + + // 棋盘处理过程 + void ClearBoard(void) { // 棋盘初始化 + sdPlayer = 0; + memset(ucpcSquares, 0, 256); + memset(ucsqPieces, 0, 48); + zobr.InitZero(); + dwBitPiece = 0; + memset(wBitRanks, 0, 16 * sizeof(uint16_t)); + memset(wBitFiles, 0, 16 * sizeof(uint16_t)); + vlWhite = vlBlack = 0; + // "ClearBoard()"后面紧跟的是"SetIrrev()",来初始化其它成员 + } + void ChangeSide(void) { // 交换走棋方 + sdPlayer = OPP_SIDE(sdPlayer); + zobr.Xor(PreGen.zobrPlayer); + } + void SaveStatus(void) { // 保存状态 + RollbackStruct *lprbs; + lprbs = rbsList + nMoveNum; + lprbs->zobr = zobr; + lprbs->vlWhite = vlWhite; + lprbs->vlBlack = vlBlack; + } + void Rollback(void) { // 回滚 + RollbackStruct *lprbs; + lprbs = rbsList + nMoveNum; + zobr = lprbs->zobr; + vlWhite = lprbs->vlWhite; + vlBlack = lprbs->vlBlack; + } + void AddPiece(int sq, int pc, bool bDel = false); // 棋盘上增加棋子 + int MovePiece(int mv); // 移动棋子 + void UndoMovePiece(int mv, int pcCaptured); // 撤消移动棋子 + int Promote(int sq); // 升变 + void UndoPromote(int sq, int pcCaptured); // 撤消升变 + + // 着法处理过程 + bool MakeMove(int mv); // 执行一个着法 + void UndoMakeMove(void); // 撤消一个着法 + void NullMove(void); // 执行一个空着 + void UndoNullMove(void); // 撤消一个空着 + void SetIrrev(void) { // 把局面设成“不可逆”,即清除回滚着法 + rbsList[0].mvs.dwmv = 0; // wmv, Chk, CptDrw, ChkChs = 0 + rbsList[0].mvs.ChkChs = CheckedBy(); + nMoveNum = 1; + nDistance = 0; + memset(ucRepHash, 0, REP_HASH_MASK + 1); + } + + // 局面处理过程 + void FromFen(const char *szFen); // FEN串识别 + void ToFen(char *szFen) const; // 生成FEN串 + void Mirror(void); // 局面镜像 + + // 着法检测过程 + bool GoodCap(int mv) const { // 好的吃子着法检测,这样的着法不记录到历史表和杀手着法表中 + int pcMoved, pcCaptured; + pcCaptured = ucpcSquares[DST(mv)]; + if (pcCaptured == 0) { + return false; + } + if (!Protected(OPP_SIDE(sdPlayer), DST(mv))) { + return true; + } + pcMoved = ucpcSquares[SRC(mv)]; + return SIMPLE_VALUE(pcCaptured) > SIMPLE_VALUE(pcMoved); + } + bool LegalMove(int mv) const; // 着法合理性检测,仅用在“杀手着法”的检测中 + int CheckedBy(bool bLazy = false) const; // 被哪个子将军 + bool IsMate(void); // 判断是已被将死 + MoveStruct LastMove(void) const { // 前一步着法,该着法保存了局面的将军状态 + return rbsList[nMoveNum - 1].mvs; + } + bool CanPromote(void) const { // 判断是否能升变 + return (wBitPiece[sdPlayer] & PAWN_BITPIECE) != PAWN_BITPIECE && LastMove().ChkChs <= 0; + } + bool NullOkay(void) const { // 允许使用空着裁剪的条件 + return (sdPlayer == 0 ? vlWhite : vlBlack) > NULLOKAY_MARGIN; + } + bool NullSafe(void) const { // 空着裁剪可以不检验的条件 + return (sdPlayer == 0 ? vlWhite : vlBlack) > NULLSAFE_MARGIN; + } + bool IsDraw(void) const { // 和棋判断 + return (!PreEval.bPromotion && (dwBitPiece & BOTH_BITPIECE(ATTACK_BITPIECE)) == 0) || + -LastMove().CptDrw >= DRAW_MOVES || nMoveNum == MAX_MOVE_NUM; + } + int RepStatus(int nRecur = 1) const; // 重复局面检测 + int DrawValue(void) const { // 和棋的分值 + return (nDistance & 1) == 0 ? -DRAW_VALUE : DRAW_VALUE; + } + int RepValue(int vlRep) const { // 重复局面的分值 + // return vlRep == REP_LOSS ? nDistance - MATE_VALUE : vlRep == REP_WIN ? MATE_VALUE - nDistance : DrawValue(); + // 长将判负的分值,低于BAN_VALUE将不写入置换表(参阅"hash.cpp") + return vlRep == REP_LOSS ? nDistance - BAN_VALUE : vlRep == REP_WIN ? BAN_VALUE - nDistance : DrawValue(); + } + int Material(void) const { // 子力平衡,包括先行权因素 + return SIDE_VALUE(sdPlayer, vlWhite - vlBlack) + PreEval.vlAdvanced; + } + + // 着法生成过程,由于这些过程代码量特别大,所以把他们都集中在"genmoves.cpp"中 + bool Protected(int sd, int sqSrc, int sqExcept = 0) const; // 棋子保护判断 + int ChasedBy(int mv) const; // 捉哪个子 + int MvvLva(int sqDst, int pcCaptured, int nLva) const; // 计算MVV(LVA)值 + int GenCapMoves(MoveStruct *lpmvs) const; // 吃子着法生成器 + int GenNonCapMoves(MoveStruct *lpmvs) const; // 不吃子着法生成器 + int GenAllMoves(MoveStruct *lpmvs) const { // 全部着法生成器 + int nCapNum; + nCapNum = GenCapMoves(lpmvs); + return nCapNum + GenNonCapMoves(lpmvs + nCapNum); + } +}; // pos + +#endif diff --git a/eleeye/pregen.cpp b/eleeye/pregen.cpp new file mode 100644 index 0000000..81b105f --- /dev/null +++ b/eleeye/pregen.cpp @@ -0,0 +1,411 @@ +/* +pregen.h/pregen.cpp - Source Code for ElephantEye, Part II + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.0, Last Modified: Nov. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "../base/base.h" +#include "pregen.h" + +const bool cbcInBoard[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +const bool cbcInFort[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +const bool cbcCanPromote[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +const int8_t ccLegalSpanTab[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 +}; + +const int8_t ccKnightPinTab[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,-16, 0,-16, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 +}; + +PreGenStruct PreGen; +PreEvalStruct PreEval; + +// 这四个数组用来判断棋子的走子方向,以马为例就是:sqDst = sqSrc + cnKnightMoveTab[i] +static const int cnKingMoveTab[4] = {-0x10, -0x01, +0x01, +0x10}; +static const int cnAdvisorMoveTab[4] = {-0x11, -0x0f, +0x0f, +0x11}; +static const int cnBishopMoveTab[4] = {-0x22, -0x1e, +0x1e, +0x22}; +static const int cnKnightMoveTab[8] = {-0x21, -0x1f, -0x12, -0x0e, +0x0e, +0x12, +0x1f, +0x21}; + +void PreGenInit(void) { + int i, j, k, n, sqSrc, sqDst; + RC4Struct rc4; + SlideMoveStruct smv; + SlideMaskStruct sms; + + // 首先初始化Zobrist键值表 + rc4.InitZero(); + PreGen.zobrPlayer.InitRC4(rc4); + for (i = 0; i < 14; i ++) { + for (j = 0; j < 256; j ++) { + PreGen.zobrTable[i][j].InitRC4(rc4); + } + } + + // 然后初始化屏蔽位行和屏蔽位列 + // 注:位行和位列不包括棋盘以外的位,所以就会频繁使用"+/- RANK_TOP/FILE_LEFT" + for (sqSrc = 0; sqSrc < 256; sqSrc ++) { + if (IN_BOARD(sqSrc)) { + PreGen.wBitRankMask[sqSrc] = 1 << (FILE_X(sqSrc) - FILE_LEFT); + PreGen.wBitFileMask[sqSrc] = 1 << (RANK_Y(sqSrc) - RANK_TOP); + } else { + PreGen.wBitRankMask[sqSrc] = 0; + PreGen.wBitFileMask[sqSrc] = 0; + } + } + + // 然后生成车炮横向的预置数组(数组的应用参阅"pregen.h") + for (i = 0; i < 9; i ++) { + for (j = 0; j < 512; j ++) { + // 初始化借助于“位行”的车和炮的着法预生成数组,包括以下几个步骤: + // 1. 初始化临时变量"SlideMoveTab",假设没有着法,用起始格填充 + smv.ucNonCap[0] = smv.ucNonCap[1] = smv.ucRookCap[0] = smv.ucRookCap[1] = + smv.ucCannonCap[0] = smv.ucCannonCap[1] = smv.ucSuperCap[0] = smv.ucSuperCap[1] = i + FILE_LEFT; + sms.wNonCap = sms.wRookCap = sms.wCannonCap = sms.wSuperCap = 0; + // 提示:参阅"pregen.h",...[0]表示最大一格,向右移动和下移动都用[0],反之亦然 + // 2. 考虑向右移动的目标格,填充...[0], + for (k = i + 1; k <= 8; k ++) { + if ((j & (1 << k)) != 0) { + smv.ucRookCap[0] = FILE_DISP(k + FILE_LEFT); + sms.wRookCap |= 1 << k; + break; + } + smv.ucNonCap[0] = FILE_DISP(k + FILE_LEFT); + sms.wNonCap |= 1 << k; + } + for (k ++; k <= 8; k ++) { + if ((j & (1 << k)) != 0) { + smv.ucCannonCap[0] = FILE_DISP(k + FILE_LEFT); + sms.wCannonCap |= 1 << k; + break; + } + } + for (k ++; k <= 8; k ++) { + if ((j & (1 << k)) != 0) { + smv.ucSuperCap[0] = FILE_DISP(k + FILE_LEFT); + sms.wSuperCap |= 1 << k; + break; + } + } + // 3. 考虑向左移动的目标格,填充...[1] + for (k = i - 1; k >= 0; k --) { + if ((j & (1 << k)) != 0) { + smv.ucRookCap[1] = FILE_DISP(k + FILE_LEFT); + sms.wRookCap |= 1 << k; + break; + } + smv.ucNonCap[1] = FILE_DISP(k + FILE_LEFT); + sms.wNonCap |= 1 << k; + } + for (k --; k >= 0; k --) { + if ((j & (1 << k)) != 0) { + smv.ucCannonCap[1] = FILE_DISP(k + FILE_LEFT); + sms.wCannonCap |= 1 << k; + break; + } + } + for (k --; k >= 0; k --) { + if ((j & (1 << k)) != 0) { + smv.ucSuperCap[1] = FILE_DISP(k + FILE_LEFT); + sms.wSuperCap |= 1 << k; + break; + } + } + // 4. 为"smv"和"sms"的值作断言 + __ASSERT_BOUND_2(3, smv.ucNonCap[1], smv.ucNonCap[0], 11); + __ASSERT_BOUND_2(3, smv.ucRookCap[1], smv.ucRookCap[0], 11); + __ASSERT_BOUND_2(3, smv.ucCannonCap[1], smv.ucCannonCap[0], 11); + __ASSERT_BOUND_2(3, smv.ucSuperCap[1], smv.ucSuperCap[0], 11); + __ASSERT_BITRANK(sms.wNonCap); + __ASSERT_BITRANK(sms.wRookCap); + __ASSERT_BITRANK(sms.wCannonCap); + __ASSERT_BITRANK(sms.wSuperCap); + // 5. 将临时变量"smv"和"sms"拷贝到着法预生成数组中 + PreGen.smvRankMoveTab[i][j] = smv; + PreGen.smsRankMaskTab[i][j] = sms; + } + } + + // 然后生成车炮纵向的预置数组(数组的应用参阅"pregen.h") + for (i = 0; i < 10; i ++) { + for (j = 0; j < 1024; j ++) { + // 初始化借助于“位列”的车和炮的着法预生成数组,包括以下几个步骤: + // 1. 初始化临时变量"smv",假设没有着法,用起始格填充 + smv.ucNonCap[0] = smv.ucNonCap[1] = smv.ucRookCap[0] = smv.ucRookCap[1] = + smv.ucCannonCap[0] = smv.ucCannonCap[1] = smv.ucSuperCap[0] = smv.ucSuperCap[1] = (i + RANK_TOP) * 16; + sms.wNonCap = sms.wRookCap = sms.wCannonCap = sms.wSuperCap = 0; + // 2. 考虑向下移动的目标格,填充...[0] + for (k = i + 1; k <= 9; k ++) { + if ((j & (1 << k)) != 0) { + smv.ucRookCap[0] = RANK_DISP(k + RANK_TOP); + sms.wRookCap |= 1 << k; + break; + } + smv.ucNonCap[0] = RANK_DISP(k + RANK_TOP); + sms.wNonCap |= 1 << k; + } + for (k ++; k <= 9; k ++) { + if ((j & (1 << k)) != 0) { + smv.ucCannonCap[0] = RANK_DISP(k + RANK_TOP); + sms.wCannonCap |= 1 << k; + break; + } + } + for (k ++; k <= 9; k ++) { + if ((j & (1 << k)) != 0) { + smv.ucSuperCap[0] = RANK_DISP(k + RANK_TOP); + sms.wSuperCap |= 1 << k; + break; + } + } + // 3. 考虑向上移动的目标格,填充...[1] + for (k = i - 1; k >= 0; k --) { + if ((j & (1 << k)) != 0) { + smv.ucRookCap[1] = RANK_DISP(k + RANK_TOP); + sms.wRookCap |= 1 << k; + break; + } + smv.ucNonCap[1] = RANK_DISP(k + RANK_TOP); + sms.wNonCap |= 1 << k; + } + for (k --; k >= 0; k --) { + if ((j & (1 << k)) != 0) { + smv.ucCannonCap[1] = RANK_DISP(k + RANK_TOP); + sms.wCannonCap |= 1 << k; + break; + } + } + for (k --; k >= 0; k --) { + if ((j & (1 << k)) != 0) { + smv.ucSuperCap[1] = RANK_DISP(k + RANK_TOP); + sms.wSuperCap |= 1 << k; + break; + } + } + // 4. 为"smv"和"sms"的值作断言 + __ASSERT_BOUND_2(3, smv.ucNonCap[1] >> 4, smv.ucNonCap[0] >> 4, 12); + __ASSERT_BOUND_2(3, smv.ucRookCap[1] >> 4, smv.ucRookCap[0] >> 4, 12); + __ASSERT_BOUND_2(3, smv.ucCannonCap[1] >> 4, smv.ucCannonCap[0] >> 4, 12); + __ASSERT_BOUND_2(3, smv.ucSuperCap[1] >> 4, smv.ucSuperCap[0] >> 4, 12); + __ASSERT_BITFILE(sms.wNonCap); + __ASSERT_BITFILE(sms.wRookCap); + __ASSERT_BITFILE(sms.wCannonCap); + __ASSERT_BITFILE(sms.wSuperCap); + // 5. 将临时变量"smv"和"sms"拷贝到着法预生成数组中 + PreGen.smvFileMoveTab[i][j] = smv; + PreGen.smsFileMaskTab[i][j] = sms; + } + } + + // 接下来生成着法预生成数组,连同将军预判数组 + for (sqSrc = 0; sqSrc < 256; sqSrc ++) { + if (IN_BOARD(sqSrc)) { + // 生成帅(将)的着法预生成数组 + n = 0; + for (i = 0; i < 4; i ++) { + sqDst = sqSrc + cnKingMoveTab[i]; + if (IN_FORT(sqDst)) { + PreGen.ucsqKingMoves[sqSrc][n] = sqDst; + n ++; + } + } + __ASSERT(n <= 4); + PreGen.ucsqKingMoves[sqSrc][n] = 0; + // 生成仕(士)的着法预生成数组 + n = 0; + for (i = 0; i < 4; i ++) { + sqDst = sqSrc + cnAdvisorMoveTab[i]; + if (IN_FORT(sqDst)) { + PreGen.ucsqAdvisorMoves[sqSrc][n] = sqDst; + n ++; + } + } + __ASSERT(n <= 4); + PreGen.ucsqAdvisorMoves[sqSrc][n] = 0; + // 生成相(象)的着法预生成数组,包括象眼数组 + n = 0; + for (i = 0; i < 4; i ++) { + sqDst = sqSrc + cnBishopMoveTab[i]; + if (IN_BOARD(sqDst) && SAME_HALF(sqSrc, sqDst)) { + PreGen.ucsqBishopMoves[sqSrc][n] = sqDst; + PreGen.ucsqBishopPins[sqSrc][n] = BISHOP_PIN(sqSrc, sqDst); + n ++; + } + } + __ASSERT(n <= 4); + PreGen.ucsqBishopMoves[sqSrc][n] = 0; + // 生成马的着法预生成数组,包括马腿数组 + n = 0; + for (i = 0; i < 8; i ++) { + sqDst = sqSrc + cnKnightMoveTab[i]; + if (IN_BOARD(sqDst)) { + PreGen.ucsqKnightMoves[sqSrc][n] = sqDst; + PreGen.ucsqKnightPins[sqSrc][n] = KNIGHT_PIN(sqSrc, sqDst); + n ++; + } + } + __ASSERT(n <= 8); + PreGen.ucsqKnightMoves[sqSrc][n] = 0; + // 生成兵(卒)的着法预生成数组 + for (i = 0; i < 2; i ++) { + n = 0; + sqDst = SQUARE_FORWARD(sqSrc, i); + sqDst = sqSrc + (i == 0 ? -16 : 16); + if (IN_BOARD(sqDst)) { + PreGen.ucsqPawnMoves[i][sqSrc][n] = sqDst; + n ++; + } + if (AWAY_HALF(sqSrc, i)) { + for (j = -1; j <= 1; j += 2) { + sqDst = sqSrc + j; + if (IN_BOARD(sqDst)) { + PreGen.ucsqPawnMoves[i][sqSrc][n] = sqDst; + n ++; + } + } + } + __ASSERT(n <= 3); + PreGen.ucsqPawnMoves[i][sqSrc][n] = 0; + } + } + } + + // 最后清空局面预评价结构 + memset(&PreEval, 0, sizeof(PreEvalStruct)); + PreEval.bPromotion = false; // 缺省是不允许升变的 +} diff --git a/eleeye/pregen.h b/eleeye/pregen.h new file mode 100644 index 0000000..7a9b799 --- /dev/null +++ b/eleeye/pregen.h @@ -0,0 +1,239 @@ +/* +pregen.h/pregen.cpp - Source Code for ElephantEye, Part II + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.0, Last Modified: Nov. 2007 +Copyright (C) 2004-2007 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../base/base.h" +#include "../base/rc4prng.h" + +#ifndef PREGEN_H +#define PREGEN_H + +#define __ASSERT_PIECE(pc) __ASSERT((pc) >= 16 && (pc) <= 47) +#define __ASSERT_SQUARE(sq) __ASSERT(IN_BOARD(sq)) +#define __ASSERT_BITRANK(w) __ASSERT((w) < (1 << 9)) +#define __ASSERT_BITFILE(w) __ASSERT((w) < (1 << 10)) + +const int RANK_TOP = 3; +const int RANK_BOTTOM = 12; +const int FILE_LEFT = 3; +const int FILE_CENTER = 7; +const int FILE_RIGHT = 11; + +extern const bool cbcInBoard[256]; // 棋盘区域表 +extern const bool cbcInFort[256]; // 城池区域表 +extern const bool cbcCanPromote[256]; // 升变区域表 +extern const int8_t ccLegalSpanTab[512]; // 合理着法跨度表 +extern const int8_t ccKnightPinTab[512]; // 马腿表 + +inline bool IN_BOARD(int sq) { + return cbcInBoard[sq]; +} + +inline bool IN_FORT(int sq) { + return cbcInFort[sq]; +} + +inline bool CAN_PROMOTE(int sq) { + return cbcCanPromote[sq]; +} + +inline int8_t LEGAL_SPAN_TAB(int nDisp) { + return ccLegalSpanTab[nDisp]; +} + +inline int8_t KNIGHT_PIN_TAB(int nDisp) { + return ccKnightPinTab[nDisp]; +} + +inline int RANK_Y(int sq) { + return sq >> 4; +} + +inline int FILE_X(int sq) { + return sq & 15; +} + +inline int COORD_XY(int x, int y) { + return x + (y << 4); +} + +inline int SQUARE_FLIP(int sq) { + return 254 - sq; +} + +inline int FILE_FLIP(int x) { + return 14 - x; +} + +inline int RANK_FLIP(int y) { + return 15 - y; +} + +inline int OPP_SIDE(int sd) { + return 1 - sd; +} + +inline int SQUARE_FORWARD(int sq, int sd) { + return sq - 16 + (sd << 5); +} + +inline int SQUARE_BACKWARD(int sq, int sd) { + return sq + 16 - (sd << 5); +} + +inline bool KING_SPAN(int sqSrc, int sqDst) { + return LEGAL_SPAN_TAB(sqDst - sqSrc + 256) == 1; +} + +inline bool ADVISOR_SPAN(int sqSrc, int sqDst) { + return LEGAL_SPAN_TAB(sqDst - sqSrc + 256) == 2; +} + +inline bool BISHOP_SPAN(int sqSrc, int sqDst) { + return LEGAL_SPAN_TAB(sqDst - sqSrc + 256) == 3; +} + +inline int BISHOP_PIN(int sqSrc, int sqDst) { + return (sqSrc + sqDst) >> 1; +} + +inline int KNIGHT_PIN(int sqSrc, int sqDst) { + return sqSrc + KNIGHT_PIN_TAB(sqDst - sqSrc + 256); +} + +inline bool WHITE_HALF(int sq) { + return (sq & 0x80) != 0; +} + +inline bool BLACK_HALF(int sq) { + return (sq & 0x80) == 0; +} + +inline bool HOME_HALF(int sq, int sd) { + return (sq & 0x80) != (sd << 7); +} + +inline bool AWAY_HALF(int sq, int sd) { + return (sq & 0x80) == (sd << 7); +} + +inline bool SAME_HALF(int sqSrc, int sqDst) { + return ((sqSrc ^ sqDst) & 0x80) == 0; +} + +inline bool DIFF_HALF(int sqSrc, int sqDst) { + return ((sqSrc ^ sqDst) & 0x80) != 0; +} + +inline int RANK_DISP(int y) { + return y << 4; +} + +inline int FILE_DISP(int x) { + return x; +} + +// 借助“位行”和“位列”生成车炮着法的预置结构 +struct SlideMoveStruct { + uint8_t ucNonCap[2]; // 不吃子能走到的最大一格/最小一格 + uint8_t ucRookCap[2]; // 车吃子能走到的最大一格/最小一格 + uint8_t ucCannonCap[2]; // 炮吃子能走到的最大一格/最小一格 + uint8_t ucSuperCap[2]; // 超级炮(隔两子吃子)能走到的最大一格/最小一格 +}; // smv + +// 借助“位行”和“位列”判断车炮着法合理性的预置结构 +struct SlideMaskStruct { + uint16_t wNonCap, wRookCap, wCannonCap, wSuperCap; +}; // sms + +struct ZobristStruct { + uint32_t dwKey, dwLock0, dwLock1; + void InitZero(void) { + dwKey = dwLock0 = dwLock1 = 0; + } + void InitRC4(RC4Struct &rc4) { + dwKey = rc4.NextLong(); + dwLock0 = rc4.NextLong(); + dwLock1 = rc4.NextLong(); + } + void Xor(const ZobristStruct &zobr) { + dwKey ^= zobr.dwKey; + dwLock0 ^= zobr.dwLock0; + dwLock1 ^= zobr.dwLock1; + } + void Xor(const ZobristStruct &zobr1, const ZobristStruct &zobr2) { + dwKey ^= zobr1.dwKey ^ zobr2.dwKey; + dwLock0 ^= zobr1.dwLock0 ^ zobr2.dwLock0; + dwLock1 ^= zobr1.dwLock1 ^ zobr2.dwLock1; + } +}; // zobr + +extern struct PreGenStruct { + // Zobrist键值表,分Zobrist键值和Zobrist校验锁两部分 + ZobristStruct zobrPlayer; + ZobristStruct zobrTable[14][256]; + + uint16_t wBitRankMask[256]; // 每个格子的位行的屏蔽位 + uint16_t wBitFileMask[256]; // 每个格子的位列的屏蔽位 + + /* 借助“位行”和“位列”生成车炮着法和判断车炮着法合理性的预置数组 + * + * “位行”和“位列”技术是ElephantEye的核心技术,用来处理车和炮的着法生成、将军判断和局面分析。 + * 以初始局面红方右边的炮在该列的行动为例,首先必须知道该列的“位列”,即"1010000101b", + * ElephantEye有两种预置数组,即"...MoveTab"和"...MaskTab",用法分别是: + * 一、如果要知道该子向前吃子的目标格(起始格是2,目标格是9),那么希望查表就能知道这个格子, + *   预先生成一个数组"FileMoveTab_CannonCap[10][1024]",使得"FileMoveTab_CannonCap[2][1010000101b] == 9"就可以了。 + * 二、如果要判断该子能否吃到目标格(同样以起始格是2,目标格是9为例),那么需要知道目标格的位列,即"0000000001b", + *   只要把"...MoveTab"的格子以“屏蔽位”的形式重新记作数组"...MaskTab"就可以了,用“与”操作来判断能否吃到目标格, + *   通常一个"...MaskTab"单元会包括多个屏蔽位,判断能否吃到同行或同列的某个格子时,只需要做一次判断就可以了。 + */ + SlideMoveStruct smvRankMoveTab[9][512]; // 36,864 字节 + SlideMoveStruct smvFileMoveTab[10][1024]; // 81,920 字节 + SlideMaskStruct smsRankMaskTab[9][512]; // 36,864 字节 + SlideMaskStruct smsFileMaskTab[10][1024]; // 81,920 字节 + // 共计: 237,568 字节 + + /* 其余棋子(不适合用“位行”和“位列”)的着法预生成数组 + * + * 这部分数组是真正意义上的“着法预生成”数组,可以根据某个棋子的起始格直接查数组,得到所有的目标格。 + * 使用数组时,可以根据起始格来确定一个指针"g_...Moves[Square]",这个指针指向一系列目标格,以0结束。 + * 为了对齐地址,数组[256][n]中n总是4的倍数,而且必须大于n(因为数组包括了结束标识符0),除了象眼和马腿数组。 + */ + uint8_t ucsqKingMoves[256][8]; + uint8_t ucsqAdvisorMoves[256][8]; + uint8_t ucsqBishopMoves[256][8]; + uint8_t ucsqBishopPins[256][4]; + uint8_t ucsqKnightMoves[256][12]; + uint8_t ucsqKnightPins[256][8]; + uint8_t ucsqPawnMoves[2][256][4]; +} PreGen; + +// 局面预评价结构 +extern struct PreEvalStruct { + bool bPromotion; + int vlAdvanced; + uint8_t ucvlWhitePieces[7][256]; + uint8_t ucvlBlackPieces[7][256]; +} PreEval; + +void PreGenInit(void); + +#endif diff --git a/eleeye/search.cpp b/eleeye/search.cpp new file mode 100644 index 0000000..ef42da9 --- /dev/null +++ b/eleeye/search.cpp @@ -0,0 +1,826 @@ +/* +search.h/search.cpp - Source Code for ElephantEye, Part VIII + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.15, Last Modified: Jul. 2008 +Copyright (C) 2004-2008 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "../base/base2.h" +#include "ucci.h" +#include "pregen.h" +#include "position.h" +#include "hash.h" +#include "book.h" +#include "movesort.h" +#include "search.h" + +const int IID_DEPTH = 2; // 内部迭代加深的深度 +const int SMP_DEPTH = 6; // 并行搜索的深度 +const int UNCHANGED_DEPTH = 4; // 未改变最佳着法的深度 + +const int DROPDOWN_VALUE = 20; // 落后的分值 +const int RESIGN_VALUE = 300; // 认输的分值 +const int DRAW_OFFER_VALUE = 40; // 提和的分值 + +SearchStruct Search; + +// 搜索信息,是封装在模块内部的 +static struct { + int64_t llTime; // 计时器 + bool bStop, bPonderStop; // 中止信号和后台思考认为的中止信号 + bool bPopPv, bPopCurrMove; // 是否输出pv和currmove + int nPopDepth, vlPopValue; // 输出的深度和分值 + int nAllNodes, nMainNodes; // 总结点数和主搜索树的结点数 + int nUnchanged; // 未改变最佳着法的深度 + uint16_t wmvPvLine[MAX_MOVE_NUM]; // 主要变例路线上的着法列表 + uint16_t wmvKiller[LIMIT_DEPTH][2]; // 杀手着法表 + MoveSortStruct MoveSort; // 根结点的着法序列 +} Search2; + +void BuildPos(PositionStruct &pos, const UcciCommStruct &UcciComm) { + int i, mv; + pos.FromFen(UcciComm.szFenStr); + for (i = 0; i < UcciComm.nMoveNum; i ++) { + mv = COORD_MOVE(UcciComm.lpdwMovesCoord[i]); + if (mv == 0) { + break; + } + if (pos.ucpcSquares[SRC(mv)] == 0) { + break; + } + pos.MakeMove(mv); + } +} + +// 中断例程 +static bool Interrupt(void) { + UcciCommStruct UcciComm; + PositionStruct posProbe; + if (Search.bIdle) { + Idle(); + } + switch (Search.nGoMode) { + case GO_MODE_NODES: + if (Search2.nAllNodes > Search.nNodes) { + Search2.bStop = true; + return true; + } + break; + case GO_MODE_TIMER: + if (!Search.bPonder && (int) (GetTime() - Search2.llTime) > Search.nMaxTimer) { + Search2.bStop = true; + return true; + } + break; + default: + break; + } + if (Search.bBatch) { + return false; + } + + // 如果不是批处理模式,那么先调用UCCI解释程序,再判断是否中止 + switch (BusyLine(UcciComm, Search.bDebug)) { + case UCCI_COMM_ISREADY: + // "isready"指令实际上没有意义 + printf("readyok\n"); + fflush(stdout); + return false; + case UCCI_COMM_PONDERHIT: + // "ponderhit"指令启动计时功能,如果"SearchMain()"例程认为已经搜索了足够的时间, 那么发出中止信号 + if (Search2.bPonderStop) { + Search2.bStop = true; + return true; + } else { + Search.bPonder = false; + return false; + } + case UCCI_COMM_PONDERHIT_DRAW: + // "ponderhit draw"指令启动计时功能,并设置提和标志 + Search.bDraw = true; + if (Search2.bPonderStop) { + Search2.bStop = true; + return true; + } else { + Search.bPonder = false; + return false; + } + case UCCI_COMM_STOP: + // "stop"指令发送中止信号 + Search2.bStop = true; + return true; + case UCCI_COMM_PROBE: + // "probe"指令输出Hash表信息 + BuildPos(posProbe, UcciComm); + PopHash(posProbe); + return false; + case UCCI_COMM_QUIT: + // "quit"指令发送退出信号 + Search.bQuit = Search2.bStop = true; + return true; + default: + return false; + } +} + +// 输出主要变例 +static void PopPvLine(int nDepth = 0, int vl = 0) { + uint16_t *lpwmv; + uint32_t dwMoveStr; + // 如果尚未达到需要输出的深度,那么记录该深度和分值,以后再输出 + if (nDepth > 0 && !Search2.bPopPv && !Search.bDebug) { + Search2.nPopDepth = nDepth; + Search2.vlPopValue = vl; + return; + } + // 输出时间和搜索结点数 + printf("info time %d nodes %d\n", (int) (GetTime() - Search2.llTime), Search2.nAllNodes); + fflush(stdout); + if (nDepth == 0) { + // 如果是搜索结束后的输出,并且已经输出过,那么不必再输出 + if (Search2.nPopDepth == 0) { + return; + } + // 获取以前没有输出的深度和分值 + nDepth = Search2.nPopDepth; + vl = Search2.vlPopValue; + } else { + // 达到需要输出的深度,那么以后不必再输出 + Search2.nPopDepth = Search2.vlPopValue = 0; + } + printf("info depth %d score %d pv", nDepth, vl); + lpwmv = Search2.wmvPvLine; + while (*lpwmv != 0) { + dwMoveStr = MOVE_COORD(*lpwmv); + printf(" %.4s", (const char *) &dwMoveStr); + lpwmv ++; + } + printf("\n"); + fflush(stdout); +} + +// 无害裁剪 +static int HarmlessPruning(const PositionStruct &pos, int vlBeta) { + int vl, vlRep; + + // 1. 杀棋步数裁剪; + vl = pos.nDistance - MATE_VALUE; + if (vl >= vlBeta) { + return vl; + } + + // 2. 和棋裁剪; + if (pos.IsDraw()) { + return 0; // 安全起见,这里不用"pos.DrawValue()"; + } + + // 3. 重复裁剪; + vlRep = pos.RepStatus(); + if (vlRep > 0) { + return pos.RepValue(vlRep); + } + + return -MATE_VALUE; +} + +// 调整型局面评价函数 +inline int Evaluate(const PositionStruct &pos, int vlAlpha, int vlBeta) { + int vl; + vl = Search.bKnowledge ? Search.Evaluate(&pos, vlAlpha, vlBeta) : pos.Material(); + return vl == pos.DrawValue() ? vl - 1 : vl; +} + +// 静态搜索例程 +static int SearchQuiesc(PositionStruct &pos, int vlAlpha, int vlBeta) { + int vlBest, vl, mv; + bool bInCheck; + MoveSortStruct MoveSort; + // 静态搜索例程包括以下几个步骤: + Search2.nAllNodes ++; + + // 1. 无害裁剪; + vl = HarmlessPruning(pos, vlBeta); + if (vl > -MATE_VALUE) { + return vl; + } + +#ifdef HASH_QUIESC + // 3. 置换裁剪; + vl = ProbeHashQ(pos, vlAlpha, vlBeta); + if (Search.bUseHash && vl > -MATE_VALUE) { + return vl; + } +#endif + + // 3-a. 连将杀检验(如果没被将军并且上一步没将军对方,则返回胜利分值); + if (Search.bAlwaysCheck && pos.LastMove().ChkChs <= 0 && + pos.rbsList[pos.nMoveNum - 2].mvs.ChkChs <= 0) { + return MATE_VALUE - pos.nDistance; + } + + // 4. 达到极限深度,直接返回评价值; + if (pos.nDistance == LIMIT_DEPTH) { + return Evaluate(pos, vlAlpha, vlBeta); + } + __ASSERT(Search.pos.nDistance < LIMIT_DEPTH); + + // 5. 初始化; + vlBest = -MATE_VALUE; + bInCheck = (pos.LastMove().ChkChs > 0); + + // 6. 对于被将军的局面,生成全部着法; + if (bInCheck) { + MoveSort.InitAll(pos); + } else { + + // 7. 对于未被将军的局面,在生成着法前首先尝试空着(空着启发),即对局面作评价; + vl = Evaluate(pos, vlAlpha, vlBeta); + __ASSERT_BOUND(1 - WIN_VALUE, vl, WIN_VALUE - 1); + __ASSERT(vl > vlBest); + if (vl >= vlBeta) { +#ifdef HASH_QUIESC + RecordHashQ(pos, vl, MATE_VALUE); +#endif + return vl; + } + vlBest = vl; + vlAlpha = MAX(vl, vlAlpha); + + // 8. 对于未被将军的局面,生成并排序所有吃子着法(MVV(LVA)启发); + MoveSort.InitQuiesc(pos); + } + + // 9. 用Alpha-Beta算法搜索这些着法; + while ((mv = MoveSort.NextQuiesc(bInCheck)) != 0) { + __ASSERT(bInCheck || pos.ucpcSquares[DST(mv)] > 0); + if (pos.MakeMove(mv)) { + vl = -SearchQuiesc(pos, -vlBeta, -vlAlpha); + pos.UndoMakeMove(); + if (vl > vlBest) { + if (vl >= vlBeta) { +#ifdef HASH_QUIESC + if (vl > -WIN_VALUE && vl < WIN_VALUE) { + RecordHashQ(pos, vl, MATE_VALUE); + } +#endif + return vl; + } + vlBest = vl; + vlAlpha = MAX(vl, vlAlpha); + } + } + } + + // 10. 返回分值。 + if (vlBest == -MATE_VALUE) { + __ASSERT(pos.IsMate()); + return pos.nDistance - MATE_VALUE; + } else { +#ifdef HASH_QUIESC + if (vlBest > -WIN_VALUE && vlBest < WIN_VALUE) { + RecordHashQ(pos, vlBest > vlAlpha ? vlBest : -MATE_VALUE, vlBest); + } +#endif + return vlBest; + } +} + +// UCCI支持 - 输出叶子结点的局面信息 +void PopLeaf(PositionStruct &pos) { + int vl; + Search2.nAllNodes = 0; + vl = SearchQuiesc(pos, -MATE_VALUE, MATE_VALUE); + printf("pophash lowerbound %d depth 0 upperbound %d depth 0\n", vl, vl); + fflush(stdout); +} + +const bool NO_NULL = true; // "SearchCut()"的参数,是否禁止空着裁剪 + +// 零窗口完全搜索例程 +static int SearchCut(int vlBeta, int nDepth, bool bNoNull = false) { + int nNewDepth, vlBest, vl; + int mvHash, mv, mvEvade; + MoveSortStruct MoveSort; + // 完全搜索例程包括以下几个步骤: + + // 1. 在叶子结点处调用静态搜索; + if (nDepth <= 0) { + __ASSERT(nDepth >= -NULL_DEPTH); + return SearchQuiesc(Search.pos, vlBeta - 1, vlBeta); + } + Search2.nAllNodes ++; + + // 2. 无害裁剪; + vl = HarmlessPruning(Search.pos, vlBeta); + if (vl > -MATE_VALUE) { + return vl; + } + + // 3. 置换裁剪; + vl = ProbeHash(Search.pos, vlBeta - 1, vlBeta, nDepth, bNoNull, mvHash); + if (Search.bUseHash && vl > -MATE_VALUE) { + return vl; + } + + // 3-a. 连将杀检验(如果没被将军并且上一步没将军对方,则返回胜利分值); + if (Search.bAlwaysCheck && Search.pos.LastMove().ChkChs <= 0 && + Search.pos.rbsList[Search.pos.nMoveNum - 2].mvs.ChkChs <= 0) { + return MATE_VALUE - Search.pos.nDistance; + } + + // 4. 达到极限深度,直接返回评价值; + if (Search.pos.nDistance == LIMIT_DEPTH) { + return Evaluate(Search.pos, vlBeta - 1, vlBeta); + } + __ASSERT(Search.pos.nDistance < LIMIT_DEPTH); + + // 5. 中断调用; + Search2.nMainNodes ++; + vlBest = -MATE_VALUE; + if ((Search2.nMainNodes & Search.nCountMask) == 0 && Interrupt()) { + return vlBest; + } + + // 6. 尝试空着裁剪; + if (Search.bNullMove && !bNoNull && Search.pos.LastMove().ChkChs <= 0 && Search.pos.NullOkay()) { + Search.pos.NullMove(); + vl = -SearchCut(1 - vlBeta, nDepth - NULL_DEPTH - 1, NO_NULL); + Search.pos.UndoNullMove(); + if (vl >= vlBeta) { + if (Search.pos.NullSafe()) { + // a. 如果空着裁剪不带检验,那么记录深度至少为(NULL_DEPTH + 1); + RecordHash(Search.pos, HASH_BETA, vl, MAX(nDepth, NULL_DEPTH + 1), 0); + return vl; + } else if (SearchCut(vlBeta, nDepth - NULL_DEPTH, NO_NULL) >= vlBeta) { + // b. 如果空着裁剪带检验,那么记录深度至少为(NULL_DEPTH); + RecordHash(Search.pos, HASH_BETA, vl, MAX(nDepth, NULL_DEPTH), 0); + return vl; + } + } + } + + // 7. 初始化; + if (Search.pos.LastMove().ChkChs > 0) { + // 如果是将军局面,那么生成所有应将着法; + mvEvade = MoveSort.InitEvade(Search.pos, mvHash, Search2.wmvKiller[Search.pos.nDistance]); + } else { + // 如果不是将军局面,那么使用正常的着法列表。 + MoveSort.InitFull(Search.pos, mvHash, Search2.wmvKiller[Search.pos.nDistance]); + mvEvade = 0; + } + + // 8. 按照"MoveSortStruct::NextFull()"例程的着法顺序逐一搜索; + while ((mv = MoveSort.NextFull(Search.pos)) != 0) { + if (Search.pos.MakeMove(mv)) { + + // 9. 尝试选择性延伸; + nNewDepth = (Search.pos.LastMove().ChkChs > 0 || mvEvade != 0 ? nDepth : nDepth - 1); + + // 10. 零窗口搜索; + vl = -SearchCut(1 - vlBeta, nNewDepth); + Search.pos.UndoMakeMove(); + if (Search2.bStop) { + return vlBest; + } + + // 11. 截断判定; + if (vl > vlBest) { + vlBest = vl; + if (vl >= vlBeta) { + RecordHash(Search.pos, HASH_BETA, vlBest, nDepth, mv); + if (!MoveSort.GoodCap(Search.pos, mv)) { + SetBestMove(mv, nDepth, Search2.wmvKiller[Search.pos.nDistance]); + } + return vlBest; + } + } + } + } + + // 12. 不截断措施。 + if (vlBest == -MATE_VALUE) { + __ASSERT(Search.pos.IsMate()); + return Search.pos.nDistance - MATE_VALUE; + } else { + RecordHash(Search.pos, HASH_ALPHA, vlBest, nDepth, mvEvade); + return vlBest; + } +} + +// 连接主要变例 +static void AppendPvLine(uint16_t *lpwmvDst, uint16_t mv, const uint16_t *lpwmvSrc) { + *lpwmvDst = mv; + lpwmvDst ++; + while (*lpwmvSrc != 0) { + *lpwmvDst = *lpwmvSrc; + lpwmvSrc ++; + lpwmvDst ++; + } + *lpwmvDst = 0; +} + +/* 主要变例完全搜索例程,和零窗口完全搜索的区别有以下几点: + * + * 1. 启用内部迭代加深启发; + * 2. 不使用有负面影响的裁剪; + * 3. Alpha-Beta边界判定复杂; + * 4. PV结点要获取主要变例; + * 5. 考虑PV结点处理最佳着法的情况。 + */ +static int SearchPV(int vlAlpha, int vlBeta, int nDepth, uint16_t *lpwmvPvLine) { + int nNewDepth, nHashFlag, vlBest, vl; + int mvBest, mvHash, mv, mvEvade; + MoveSortStruct MoveSort; + uint16_t wmvPvLine[LIMIT_DEPTH]; + // 完全搜索例程包括以下几个步骤: + + // 1. 在叶子结点处调用静态搜索; + *lpwmvPvLine = 0; + if (nDepth <= 0) { + __ASSERT(nDepth >= -NULL_DEPTH); + return SearchQuiesc(Search.pos, vlAlpha, vlBeta); + } + Search2.nAllNodes ++; + + // 2. 无害裁剪; + vl = HarmlessPruning(Search.pos, vlBeta); + if (vl > -MATE_VALUE) { + return vl; + } + + // 3. 置换裁剪; + vl = ProbeHash(Search.pos, vlAlpha, vlBeta, nDepth, NO_NULL, mvHash); + if (Search.bUseHash && vl > -MATE_VALUE) { + // 由于PV结点不适用置换裁剪,所以不会发生PV路线中断的情况 + return vl; + } + + // 3-a. 连将杀检验(如果没被将军并且上一步没将军对方,则返回胜利分值); + if (Search.bAlwaysCheck && Search.pos.LastMove().ChkChs <= 0 && + Search.pos.rbsList[Search.pos.nMoveNum - 2].mvs.ChkChs <= 0) { + return MATE_VALUE - Search.pos.nDistance; + } + + // 4. 达到极限深度,直接返回评价值; + __ASSERT(Search.pos.nDistance > 0); + if (Search.pos.nDistance == LIMIT_DEPTH) { + return Evaluate(Search.pos, vlAlpha, vlBeta); + } + __ASSERT(Search.pos.nDistance < LIMIT_DEPTH); + + // 5. 中断调用; + Search2.nMainNodes ++; + vlBest = -MATE_VALUE; + if ((Search2.nMainNodes & Search.nCountMask) == 0 && Interrupt()) { + return vlBest; + } + + // 6. 内部迭代加深启发; + if (nDepth > IID_DEPTH && mvHash == 0) { + __ASSERT(nDepth / 2 <= nDepth - IID_DEPTH); + vl = SearchPV(vlAlpha, vlBeta, nDepth / 2, wmvPvLine); + if (vl <= vlAlpha) { + vl = SearchPV(-MATE_VALUE, vlBeta, nDepth / 2, wmvPvLine); + } + if (Search2.bStop) { + return vlBest; + } + mvHash = wmvPvLine[0]; + } + + // 7. 初始化; + mvBest = 0; + nHashFlag = HASH_ALPHA; + if (Search.pos.LastMove().ChkChs > 0) { + // 如果是将军局面,那么生成所有应将着法; + mvEvade = MoveSort.InitEvade(Search.pos, mvHash, Search2.wmvKiller[Search.pos.nDistance]); + } else { + // 如果不是将军局面,那么使用正常的着法列表。 + MoveSort.InitFull(Search.pos, mvHash, Search2.wmvKiller[Search.pos.nDistance]); + mvEvade = 0; + } + + // 8. 按照"MoveSortStruct::NextFull()"例程的着法顺序逐一搜索; + while ((mv = MoveSort.NextFull(Search.pos)) != 0) { + if (Search.pos.MakeMove(mv)) { + + // 9. 尝试选择性延伸; + nNewDepth = (Search.pos.LastMove().ChkChs > 0 || mvEvade != 0 ? nDepth : nDepth - 1); + + // 10. 主要变例搜索; + if (vlBest == -MATE_VALUE) { + vl = -SearchPV(-vlBeta, -vlAlpha, nNewDepth, wmvPvLine); + } else { + vl = -SearchCut(-vlAlpha, nNewDepth); + if (vl > vlAlpha && vl < vlBeta) { + vl = -SearchPV(-vlBeta, -vlAlpha, nNewDepth, wmvPvLine); + } + } + Search.pos.UndoMakeMove(); + if (Search2.bStop) { + return vlBest; + } + + // 11. Alpha-Beta边界判定; + if (vl > vlBest) { + vlBest = vl; + if (vl >= vlBeta) { + mvBest = mv; + nHashFlag = HASH_BETA; + break; + } + if (vl > vlAlpha) { + vlAlpha = vl; + mvBest = mv; + nHashFlag = HASH_PV; + AppendPvLine(lpwmvPvLine, mv, wmvPvLine); + } + } + } + } + + // 12. 更新置换表、历史表和杀手着法表。 + if (vlBest == -MATE_VALUE) { + __ASSERT(Search.pos.IsMate()); + return Search.pos.nDistance - MATE_VALUE; + } else { + RecordHash(Search.pos, nHashFlag, vlBest, nDepth, mvEvade == 0 ? mvBest : mvEvade); + if (mvBest != 0 && !MoveSort.GoodCap(Search.pos, mvBest)) { + SetBestMove(mvBest, nDepth, Search2.wmvKiller[Search.pos.nDistance]); + } + return vlBest; + } +} + +/* 根结点搜索例程,和完全搜索的区别有以下几点: + * + * 1. 省略无害裁剪(也不获取置换表着法); + * 2. 不使用空着裁剪; + * 3. 选择性延伸只使用将军延伸; + * 4. 过滤掉禁止着法; + * 5. 搜索到最佳着法时要做很多处理(包括记录主要变例、结点排序等); + * 6. 不更新历史表和杀手着法表。 + */ +static int SearchRoot(int nDepth) { + int nNewDepth, vlBest, vl, mv; + uint32_t dwMoveStr; + uint16_t wmvPvLine[LIMIT_DEPTH]; + // 根结点搜索例程包括以下几个步骤: + + // 1. 初始化 + vlBest = -MATE_VALUE; + Search2.MoveSort.ResetRoot(); + + // 2. 逐一搜索每个着法(要过滤禁止着法) + while ((mv = Search2.MoveSort.NextRoot()) != 0) { + if (Search.pos.MakeMove(mv)) { + if (Search2.bPopCurrMove || Search.bDebug) { + dwMoveStr = MOVE_COORD(mv); + printf("info currmove %.4s\n", (const char *) &dwMoveStr); + fflush(stdout); + } + + // 3. 尝试选择性延伸(只考虑将军延伸) + nNewDepth = (Search.pos.LastMove().ChkChs > 0 ? nDepth : nDepth - 1); + + // 4. 主要变例搜索 + if (vlBest == -MATE_VALUE) { + vl = -SearchPV(-MATE_VALUE, MATE_VALUE, nNewDepth, wmvPvLine); + __ASSERT(vl > vlBest); + } else { + vl = -SearchCut(-vlBest, nNewDepth); + if (vl > vlBest) { // 这里不需要" && vl < MATE_VALUE"了 + vl = -SearchPV(-MATE_VALUE, -vlBest, nNewDepth, wmvPvLine); + } + } + Search.pos.UndoMakeMove(); + if (Search2.bStop) { + return vlBest; + } + + // 5. Alpha-Beta边界判定("vlBest"代替了"SearchPV()"中的"vlAlpha") + if (vl > vlBest) { + + // 6. 如果搜索到第一着法,那么"未改变最佳着法"的计数器加1,否则清零 + Search2.nUnchanged = (vlBest == -MATE_VALUE ? Search2.nUnchanged + 1 : 0); + vlBest = vl; + + // 7. 搜索到最佳着法时记录主要变例 + AppendPvLine(Search2.wmvPvLine, mv, wmvPvLine); + PopPvLine(nDepth, vl); + + // 8. 如果要考虑随机性,则Alpha值要作随机浮动,但已搜索到杀棋时不作随机浮动 + if (vlBest > -WIN_VALUE && vlBest < WIN_VALUE) { + vlBest += (Search.rc4Random.NextLong() & Search.nRandomMask) - + (Search.rc4Random.NextLong() & Search.nRandomMask); + vlBest = (vlBest == Search.pos.DrawValue() ? vlBest - 1 : vlBest); + } + + // 9. 更新根结点着法列表 + Search2.MoveSort.UpdateRoot(mv); + } + } + } + return vlBest; +} + +// 唯一着法检验是ElephantEye在搜索上的一大特色,用来判断用以某种深度进行的搜索是否找到了唯一着法。 +// 其原理是把找到的最佳着法设成禁止着法,然后以(-WIN_VALUE, 1 - WIN_VALUE)的窗口重新搜索, +// 如果低出边界则说明其他着法都将被杀。 +static bool SearchUnique(int vlBeta, int nDepth) { + int vl, mv; + Search2.MoveSort.ResetRoot(ROOT_UNIQUE); + // 跳过第一个着法 + while ((mv = Search2.MoveSort.NextRoot()) != 0) { + if (Search.pos.MakeMove(mv)) { + vl = -SearchCut(1 - vlBeta, Search.pos.LastMove().ChkChs > 0 ? nDepth : nDepth - 1); + Search.pos.UndoMakeMove(); + if (Search2.bStop || vl >= vlBeta) { + return false; + } + } + } + return true; +} + +// 主搜索例程 +void SearchMain(int nDepth) { + int i, vl, vlLast, nBookMoves; + int nCurrTimer, nLimitTimer; + bool bUnique; + uint32_t dwMoveStr; + MoveStruct mvsBook[MAX_GEN_MOVES]; + // 主搜索例程包括以下几个步骤: + + // 1. 遇到和棋则直接返回 + if (Search.pos.IsDraw() || Search.pos.RepStatus(3) > 0) { + printf("nobestmove\n"); + fflush(stdout); + return; + } + + // 2. 从开局库中搜索着法 + if (Search.bUseBook) { + // a. 获取开局库中的所有走法 + nBookMoves = GetBookMoves(Search.pos, Search.szBookFile, mvsBook); + if (nBookMoves > 0) { + vl = 0; + for (i = 0; i < nBookMoves; i ++) { + vl += mvsBook[i].wvl; + dwMoveStr = MOVE_COORD(mvsBook[i].wmv); + printf("info depth 0 score %d pv %.4s\n", mvsBook[i].wvl, (const char *) &dwMoveStr); + fflush(stdout); + } + // b. 根据权重随机选择一个走法 + vl = Search.rc4Random.NextLong() % (uint32_t) vl; + for (i = 0; i < nBookMoves; i ++) { + vl -= mvsBook[i].wvl; + if (vl < 0) { + break; + } + } + __ASSERT(vl < 0); + __ASSERT(i < nBookMoves); + // c. 如果开局库中的着法够成循环局面,那么不走这个着法 + Search.pos.MakeMove(mvsBook[i].wmv); + if (Search.pos.RepStatus(3) == 0) { + dwMoveStr = MOVE_COORD(mvsBook[i].wmv); + printf("bestmove %.4s", (const char *) &dwMoveStr); + // d. 给出后台思考的着法(开局库中第一个即权重最大的后续着法) + nBookMoves = GetBookMoves(Search.pos, Search.szBookFile, mvsBook); + Search.pos.UndoMakeMove(); + if (nBookMoves > 0) { + dwMoveStr = MOVE_COORD(mvsBook[0].wmv); + printf(" ponder %.4s", (const char *) &dwMoveStr); + } + printf("\n"); + fflush(stdout); + return; + } + Search.pos.UndoMakeMove(); + } + } + + // 3. 如果深度为零则返回静态搜索值 + if (nDepth == 0) { + printf("info depth 0 score %d\n", SearchQuiesc(Search.pos, -MATE_VALUE, MATE_VALUE)); + fflush(stdout); + printf("nobestmove\n"); + fflush(stdout); + return; + } + + // 4. 生成根结点的每个着法 + Search2.MoveSort.InitRoot(Search.pos, Search.nBanMoves, Search.wmvBanList); + + // 5. 初始化时间和计数器 + Search2.bStop = Search2.bPonderStop = Search2.bPopPv = Search2.bPopCurrMove = false; + Search2.nPopDepth = Search2.vlPopValue = 0; + Search2.nAllNodes = Search2.nMainNodes = Search2.nUnchanged = 0; + Search2.wmvPvLine[0] = 0; + ClearKiller(Search2.wmvKiller); + ClearHistory(); + ClearHash(); + // 由于 ClearHash() 需要消耗一定时间,所以计时从这以后开始比较合理 + Search2.llTime = GetTime(); + vlLast = 0; + // 如果走了10回合无用着法,那么允许主动提和,以后每隔8回合提和一次 + if (Search.pos.nMoveNum > 5 && ((Search.pos.nMoveNum - 4) / 2) % 8 == 0) { + Search.bDraw = true; + } + bUnique = false; + nCurrTimer = 0; + + // 6. 做迭代加深搜索 + for (i = 1; i <= nDepth; i ++) { + // 需要输出主要变例时,第一个"info depth n"是不输出的 + if (Search2.bPopPv || Search.bDebug) { + printf("info depth %d\n", i); + fflush(stdout); + } + + // 7. 根据搜索的时间决定,是否需要输出主要变例和当前思考的着法 + Search2.bPopPv = (nCurrTimer > 300); + Search2.bPopCurrMove = (nCurrTimer > 3000); + + // 8. 搜索根结点 + vl = SearchRoot(i); + if (Search2.bStop) { + if (vl > -MATE_VALUE) { + vlLast = vl; // 跳出后,vlLast会用来判断认输或投降,所以需要给定最近一个值 + } + break; // 没有跳出,则"vl"是可靠值 + } + + nCurrTimer = (int) (GetTime() - Search2.llTime); + // 9. 如果搜索时间超过适当时限,则终止搜索 + if (Search.nGoMode == GO_MODE_TIMER) { + // a. 如果没有使用空着裁剪,那么适当时限减半(因为分枝因子加倍了) + nLimitTimer = (Search.bNullMove ? Search.nProperTimer : Search.nProperTimer / 2); + // b. 如果当前搜索值没有落后前一层很多,那么适当时限减半 + nLimitTimer = (vl + DROPDOWN_VALUE >= vlLast ? nLimitTimer / 2 : nLimitTimer); + // c. 如果最佳着法连续多层没有变化,那么适当时限减半 + nLimitTimer = (Search2.nUnchanged >= UNCHANGED_DEPTH ? nLimitTimer / 2 : nLimitTimer); + if (nCurrTimer > nLimitTimer) { + if (Search.bPonder) { + Search2.bPonderStop = true; // 如果处于后台思考模式,那么只是在后台思考命中后立即中止搜索。 + } else { + vlLast = vl; + break; // 不管是否跳出,"vlLast"都已更新 + } + } + } + vlLast = vl; + + // 10. 搜索到杀棋则终止搜索 + if (vlLast > WIN_VALUE || vlLast < -WIN_VALUE) { + break; + } + + // 11. 是唯一着法,则终止搜索 + if (SearchUnique(1 - WIN_VALUE, i)) { + bUnique = true; + break; + } + } + + // 12. 输出最佳着法及其最佳应对(作为后台思考的猜测着法) + if (Search2.wmvPvLine[0] != 0) { + PopPvLine(); + dwMoveStr = MOVE_COORD(Search2.wmvPvLine[0]); + printf("bestmove %.4s", (const char *) &dwMoveStr); + if (Search2.wmvPvLine[1] != 0) { + dwMoveStr = MOVE_COORD(Search2.wmvPvLine[1]); + printf(" ponder %.4s", (const char *) &dwMoveStr); + } + + // 13. 判断是否认输或提和,但是经过唯一着法检验的不适合认输或提和(因为搜索深度不够) + if (!bUnique) { + if (vlLast > -WIN_VALUE && vlLast < -RESIGN_VALUE) { + printf(" resign"); + } else if (Search.bDraw && !Search.pos.NullSafe() && vlLast < DRAW_OFFER_VALUE * 2) { + printf(" draw"); + } + } + } else { + printf("nobestmove"); + } + printf("\n"); + fflush(stdout); +} diff --git a/eleeye/search.h b/eleeye/search.h new file mode 100644 index 0000000..03f7dd2 --- /dev/null +++ b/eleeye/search.h @@ -0,0 +1,74 @@ +/* +search.h/search.cpp - Source Code for ElephantEye, Part VIII + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.15, Last Modified: Jul. 2008 +Copyright (C) 2004-2008 www.elephantbase.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../base/base.h" +#include "../base/rc4prng.h" +#include "ucci.h" +#include "pregen.h" +#include "position.h" + +#ifndef SEARCH_H +#define SEARCH_H + +#ifdef _WIN32 + #include +#else + #define WINAPI +#endif + +// 搜索模式 +const int GO_MODE_INFINITY = 0; +const int GO_MODE_NODES = 1; +const int GO_MODE_TIMER = 2; + +// 搜索前可设置的全局变量,指定搜索参数 +struct SearchStruct { + PositionStruct pos; // 有待搜索的局面 + bool bQuit, bPonder, bDraw; // 是否收到退出指令、后台思考模式和提和模式 + bool bBatch, bDebug; // 是否批处理模式和调试模式 + bool bUseHash, bUseBook; // 是否使用置换表裁剪和开局库 + bool bNullMove, bKnowledge; // 是否空着裁剪和使用局面评价知识 + bool bAlwaysCheck; // 是否只将军(解连将杀排局) + bool bIdle; // 是否空闲 + RC4Struct rc4Random; // 随机数 + int nGoMode, nNodes, nCountMask; // 搜索模式、结点数和 + int nProperTimer, nMaxTimer; // 计划使用时间 + int nRandomMask, nBanMoves; // 随机性屏蔽位和禁着数 + uint16_t wmvBanList[MAX_MOVE_NUM]; // 禁着列表 + char szBookFile[1024]; // 开局库 + const char *(WINAPI *GetEngineName)(void); // 局面评价引擎名称API函数指针 + void (WINAPI *PreEvaluate)(PositionStruct *, PreEvalStruct *); // 局面预评价API函数指针 + int (WINAPI *Evaluate)(const PositionStruct *, int, int); // 局面评价API函数指针 +}; + +extern SearchStruct Search; + +// UCCI局面构造过程 +void BuildPos(PositionStruct &pos, const UcciCommStruct &UcciComm); + +// UCCI支持 - 输出叶子结点的局面信息 +void PopLeaf(PositionStruct &pos); + +// 搜索的启动过程 +void SearchMain(int nDepth); + +#endif diff --git a/eleeye/ucci.cpp b/eleeye/ucci.cpp new file mode 100644 index 0000000..82cb472 --- /dev/null +++ b/eleeye/ucci.cpp @@ -0,0 +1,405 @@ +/* +ucci.h/ucci.cpp - Source Code for ElephantEye, Part I + +ElephantEye - a Chinese Chess Program (UCCI Engine) +Designed by Morning Yellow, Version: 3.15, Last Modified: Jul. 2008 +Copyright (C) 2004-2008 www.elephantbase.net + +This part (ucci.h/ucci.cpp only) of codes is NOT published under LGPL, and +can be used without restriction. +*/ + +#include +#include "../base/base2.h" +#include "../base/parse.h" +#include "../base/pipe.h" +#include "ucci.h" + +/* UCCI指令分析模块由三各UCCI指令解释器组成。 + * + * 其中第一个解释器"BootLine()"最简单,只用来接收引擎启动后的第一行指令 + * 输入"ucci"时就返回"UCCI_COMM_UCCI",否则一律返回"UCCI_COMM_UNKNOWN" + * 前两个解释器都等待是否有输入,如果没有输入则执行待机指令"Idle()" + * 而第三个解释器("BusyLine()",只用在引擎思考时)则在没有输入时直接返回"UCCI_COMM_UNKNOWN" + */ +static PipeStruct pipeStd; + +const int MAX_MOVE_NUM = 256; + +static char szFen[LINE_INPUT_MAX_CHAR]; +static uint32_t dwCoordList[MAX_MOVE_NUM]; + +static bool ParsePos(UcciCommStruct &UcciComm, char *lp) { + int i; + // 首先判断是否指定了FEN串 + if (StrEqvSkip(lp, "fen ")) { + strcpy(szFen, lp); + UcciComm.szFenStr = szFen; + // 然后判断是否是startpos + } else if (StrEqv(lp, "startpos")) { + UcciComm.szFenStr = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w - - 0 1"; + // 如果两者都不是,就立即返回 + } else { + return false; + } + // 然后寻找是否指定了后续着法,即是否有"moves"关键字 + UcciComm.nMoveNum = 0; + if (StrScanSkip(lp, " moves ")) { + *(lp - strlen(" moves ")) = '\0'; + UcciComm.nMoveNum = MIN((int) (strlen(lp) + 1) / 5, MAX_MOVE_NUM); // 提示:"moves"后面的每个着法都是1个空格和4个字符 + for (i = 0; i < UcciComm.nMoveNum; i ++) { + dwCoordList[i] = *(uint32_t *) lp; // 4个字符可转换为一个"uint32_t",存储和处理起来方便 + lp += sizeof(uint32_t) + 1; + } + UcciComm.lpdwMovesCoord = dwCoordList; + } + return true; +} + +UcciCommEnum BootLine(void) { + char szLineStr[LINE_INPUT_MAX_CHAR]; + pipeStd.Open(); + while (!pipeStd.LineInput(szLineStr)) { + Idle(); + } + if (StrEqv(szLineStr, "ucci")) { + return UCCI_COMM_UCCI; + } else { + return UCCI_COMM_UNKNOWN; + } +} + +UcciCommEnum IdleLine(UcciCommStruct &UcciComm, bool bDebug) { + char szLineStr[LINE_INPUT_MAX_CHAR]; + char *lp; + int i; + bool bGoTime; + + while (!pipeStd.LineInput(szLineStr)) { + Idle(); + } + lp = szLineStr; + if (bDebug) { + printf("info idleline [%s]\n", lp); + fflush(stdout); + } + if (false) { + // "IdleLine()"是最复杂的UCCI指令解释器,大多数的UCCI指令都由它来解释,包括: + + // 1. "isready"指令 + } else if (StrEqv(lp, "isready")) { + return UCCI_COMM_ISREADY; + + // 2. "setoption