1: 2: logic 3: { 4: // ~~~~~~~ Initial board layout 5: // 0 = empty space 6: // 1 = white pawn 7: // 3 = white knight 8: // 5 = white bishop 9: // 7 = white rook 10: // 9 = white queen 11: // 11 = white king 12: // 2 = black pawn 13: // 4 = black knight 14: // 6 = black bishop 15: // 8 = black rook 16: // 10 = black queen 17: // 12 = black king 18: 19: array board 20: { 21: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 22: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 23: -1, -1, 8, 4, 6, 10, 12, 6, 4, 8, -1, -1, 24: -1, -1, 2, 2, 2, 2, 2, 2, 2, 2, -1, -1, 25: -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 26: -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 27: -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 28: -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 29: -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 30: -1, -1, 7, 3, 5, 9, 11, 5, 3, 7, -1, -1, 31: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 32: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 33: } 34: local int boardwidth { 12 } 35: local int boardheight { 12 } 36: 37: // Display rotation - A given x,y is represented by dRotA+dRotX*x+dRotY*y 38: local array dRotA { 0, 132, 143, 11 } 39: local array dRotX { 1, -12, -1, 12 } 40: local array dRotY { 12, 1, -12, -1 } 41: local int dRot { 0 } 42: local int dRotAuto { 0 } 43: 44: // Denote the currently selected piece and who's turn it is 45: int selected { -1 } 46: int turn { 1 } 47: 48: // Are we using unicode or simple characters? 49: local int charset { 0 } 50: 51: // Special move variables 52: int enpassant { -1 } // Keep track of possible en-passant moves 53: int castlingWKmoved { 0 } // Has white king moved yet? 54: int castlingWRRmoved { 0 } // Has white rook on right moved yet? 55: int castlingWRLmoved { 0 } // Has white rook on left moved yet? 56: int castlingBKmoved { 0 } // Has black king moved yet? 57: int castlingBRRmoved { 0 } // Has black rook on right moved yet? 58: int castlingBRLmoved { 0 } // Has black rook on left moved yet? 59: 60: 61: // Previous move direction - Used by pieces that can move any number of 62: // spaces in a given direction 63: int prevmove { 0 } 64: 65: // Piece to 'promote' 66: int promotePiece { -1 } 67: 68: // Knight move list relative to current position 69: /* 70: local array knightmove 71: { 72: -2*boardwidth-1, -2*boardwidth+1, -boardwidth-2, -boardwidth+2, 73: 2*boardwidth-1, 2*boardwidth+1, boardwidth-2, boardwidth+2 74: } 75: */ 76: // For boardwidth 12 this becomes... 77: local array knightmove {-25, -23, -14, -10, 23, 25, 10, 14} 78: 79: // King move list, arranged so all the even positions are diagonal (rotate clockwise) 80: local array kingmove {-13, -12, -11, 1, 13, 12, 11, -1} 81: 82: // Scratch variable for test calculations 83: local int test {0} 84: 85: // Allow charset to be changed whenever 86: when { 1 } 87: allow { charset = (charset+1)%2; } 88: label {"switch charset"} 89: 90: // Allow board to be rotated and auto-rotate toggled whenever 91: when { 1 } 92: allow { dRot = (dRot+1)%4; } 93: label {"rotate clockwise"} 94: when { 1 } 95: allow { dRot = (dRot+3)%4; } 96: label {"rotate anticlockwise"} 97: when { 1 } 98: allow { dRotAuto = 1-dRotAuto; } 99: label {"toggle autorotate"} 100: 101: // If nothing selected, allow the player to pick any of her pieces 102: // (we don't check if a piece could be moved, that would be too complicated) 103: for i (0 .. ((boardheight*boardwidth)-1)) 104: { 105: when 106: { 107: (selected == -1) and (promotePiece == -1) and 108: (board[i] > 0) and (board[i] % 2 == turn) 109: } 110: allow {selected = i;} 111: label {"select " i} 112: } 113: 114: // If piece selected, allow all possible moves for that piece to be selected 115: // *** Pawn 116: // Normal move 117: when 118: { 119: (selected != -1) and ((board[selected] + 1) / 2 == 1) and 120: (board[selected-(((turn*2) - 1)*boardwidth)] == 0) 121: } 122: allow 123: { 124: // Store the place the pawn will be moving to 125: test = selected-(((turn*2) - 1)*boardwidth); 126: board[test] = board[selected]; 127: board[selected] = 0; 128: // Does this trigger promotion? 129: if ((test/boardwidth == 2) or (test/boardwidth == 9)) 130: { 131: selected = -1; 132: promotePiece = test; 133: } 134: // else 135: if (!((test/boardwidth == 2) or (test/boardwidth == 9))) 136: { 137: // Standard "end turn" 138: selected = -1; 139: enpassant = -1; 140: if (dRotAuto == 1) {dRot = turn*2;} 141: turn = (turn + 1) % 2; 142: } 143: } 144: label {"move " (selected-(((turn*2) - 1)*boardwidth))} 145: 146: // Start move 147: when 148: { 149: (selected != -1) and ((board[selected] + 1) / 2 == 1) and 150: ((selected/boardwidth == 3) or (selected/boardwidth == 8)) and 151: (board[selected-(((turn*2) - 1)*boardwidth)] == 0) and 152: (board[selected-(((turn*2) - 1)*boardwidth*2)] == 0) 153: } 154: allow 155: { 156: board[selected-(((turn*2) - 1)*boardwidth*2)] = board[selected]; 157: // Store the en passant variable 158: enpassant = selected-(((turn*2) - 1)*boardwidth); 159: board[selected] = 0; 160: // Standard "end turn" 161: selected = -1; 162: //enpassant = -1; 163: if (dRotAuto == 1) {dRot = turn*2;} 164: turn = (turn + 1) % 2; 165: } 166: label {"move " (selected-(((turn*2) - 1)*boardwidth*2))} 167: 168: // Take to left 169: when 170: { 171: (selected != -1) and ((board[selected] + 1) / 2 == 1) and 172: (board[(selected-(((turn*2) - 1)*boardwidth))-1] > 0) and 173: (board[(selected-(((turn*2) - 1)*boardwidth))-1] % 2 == (turn+1)%2) 174: } 175: allow 176: { 177: // Store the place the pawn will be moving to 178: test = (selected-(((turn*2) - 1)*boardwidth))-1; 179: board[test] = board[selected]; 180: board[selected] = 0; 181: // Does this trigger promotion? 182: if ((test/boardwidth == 2) or (test/boardwidth == 9)) 183: { 184: selected = -1; 185: promotePiece = test; 186: } 187: // else 188: if (!((test/boardwidth == 2) or (test/boardwidth == 9))) 189: { 190: // Standard "end turn" 191: selected = -1; 192: enpassant = -1; 193: if (dRotAuto == 1) {dRot = turn*2;} 194: turn = (turn + 1) % 2; 195: } 196: } 197: label {"move " ((selected-(((turn*2) - 1)*boardwidth))-1)} 198: 199: // Take to right 200: when 201: { 202: (selected != -1) and ((board[selected] + 1) / 2 == 1) and 203: (board[(selected-(((turn*2) - 1)*boardwidth))+1] > 0) and 204: (board[(selected-(((turn*2) - 1)*boardwidth))+1] % 2 == (turn+1)%2) 205: } 206: allow 207: { 208: // Store the place the pawn will be moving to 209: test = (selected-(((turn*2) - 1)*boardwidth))+1; 210: board[test] = board[selected]; 211: board[selected] = 0; 212: // Does this trigger promotion? 213: if ((test/boardwidth == 2) or (test/boardwidth == 9)) 214: { 215: selected = -1; 216: promotePiece = test; 217: } 218: // else 219: if (!((test/boardwidth == 2) or (test/boardwidth == 9))) 220: { 221: // Standard "end turn" 222: selected = -1; 223: enpassant = -1; 224: if (dRotAuto == 1) {dRot = turn*2;} 225: turn = (turn + 1) % 2; 226: } 227: } 228: label {"move " ((selected-(((turn*2) - 1)*boardwidth))+1)} 229: 230: // En-passant take 231: when 232: { 233: (selected != -1) and ((board[selected] + 1) / 2 == 1) 234: and 235: ( 236: ((selected-(((turn*2) - 1)*boardwidth))-1 == enpassant) or 237: ((selected-(((turn*2) - 1)*boardwidth))+1 == enpassant) 238: ) 239: } 240: allow 241: { 242: board[enpassant] = board[selected]; 243: board[enpassant+(((turn*2) - 1)*boardwidth)] = 0; 244: board[selected] = 0; 245: // Standard "end turn" 246: selected = -1; 247: enpassant = -1; 248: if (dRotAuto == 1) {dRot = turn*2;} 249: turn = (turn + 1) % 2; 250: } 251: label {"move " enpassant} 252: 253: // Promotion 254: when {promotePiece > -1} 255: allow 256: { 257: board[promotePiece] += 2; 258: promotePiece = -1; 259: // Standard "end turn" 260: selected = -1; 261: enpassant = -1; 262: if (dRotAuto == 1) {dRot = turn*2;} 263: turn = (turn + 1) % 2; 264: } 265: label {"promote to knight"} 266: 267: when {promotePiece > -1} 268: allow 269: { 270: board[promotePiece] += 4; 271: promotePiece = -1; 272: // Standard "end turn" 273: selected = -1; 274: enpassant = -1; 275: if (dRotAuto == 1) {dRot = turn*2;} 276: turn = (turn + 1) % 2; 277: } 278: label {"promote to bishop"} 279: 280: when {promotePiece > -1} 281: allow 282: { 283: board[promotePiece] += 6; 284: promotePiece = -1; 285: // Standard "end turn" 286: selected = -1; 287: enpassant = -1; 288: if (dRotAuto == 1) {dRot = turn*2;} 289: turn = (turn + 1) % 2; 290: } 291: label {"promote to rook"} 292: 293: when {promotePiece > -1} 294: allow 295: { 296: board[promotePiece] += 8; 297: promotePiece = -1; 298: // Standard "end turn" 299: selected = -1; 300: enpassant = -1; 301: if (dRotAuto == 1) {dRot = turn*2;} 302: turn = (turn + 1) % 2; 303: } 304: label {"promote to queen"} 305: 306: // *** Knight 307: for i ( 0 .. 7 ) 308: { 309: when 310: { 311: (selected != -1) and ((board[selected] + 1) / 2 == 2) and 312: ( 313: (board[selected+knightmove[i]] == 0) or 314: ( 315: (board[selected+knightmove[i]] > 0) and 316: (board[selected+knightmove[i]]%2 == (turn+1)%2) 317: ) 318: ) 319: } 320: allow 321: { 322: board[selected+knightmove[i]] = board[selected]; 323: board[selected] = 0; 324: // Standard "end turn" 325: selected = -1; 326: enpassant = -1; 327: if (dRotAuto == 1) {dRot = turn*2;} 328: turn = (turn + 1) % 2; 329: } 330: label {"move " (selected+knightmove[i])} 331: } 332: 333: // *** Bishop and Queen diagonal movement 334: for i ( 0 .. 3 ) 335: { 336: when 337: { 338: (selected != -1) and (prevmove == 0) and 339: (((board[selected] + 1) / 2 == 3) or ((board[selected] + 1) / 2 == 5)) and 340: ( 341: (board[selected+kingmove[i*2]] == 0) or 342: ( 343: (board[selected+kingmove[i*2]] > 0) and 344: (board[selected+kingmove[i*2]]%2 == (turn+1)%2) 345: ) 346: ) 347: } 348: allow 349: { 350: // Store the content of the square we are moving to 351: test = board[selected+kingmove[i*2]]; 352: 353: board[selected+kingmove[i*2]] = board[selected]; 354: board[selected] = 0; 355: 356: if (test == 0) 357: { 358: // Turn does NOT end: Ranged movement allowed 359: selected = selected+kingmove[i*2]; 360: prevmove = kingmove[i*2]; 361: } 362: if (test != 0) 363: { 364: // Standard "end turn" 365: selected = -1; 366: enpassant = -1; 367: if (dRotAuto == 1) {dRot = turn*2;} 368: turn = (turn + 1) % 2; 369: } 370: } 371: label {"move " (selected+kingmove[i*2])} 372: } 373: 374: // *** Rook and Queen straight movement 375: for i ( 0 .. 3 ) 376: { 377: when 378: { 379: (selected != -1) and (prevmove == 0) and 380: (((board[selected] + 1) / 2 == 4) or ((board[selected] + 1) / 2 == 5)) and 381: ( 382: (board[selected+kingmove[i*2+1]] == 0) or 383: ( 384: (board[selected+kingmove[i*2+1]] > 0) and 385: (board[selected+kingmove[i*2+1]]%2 == (turn+1)%2) 386: ) 387: ) 388: } 389: allow 390: { 391: // First mark that this movement has disallowed castling for this piece 392: // (Values hardcoded for 12*12 board) 393: if (selected==26) {castlingBRLmoved = 1;} 394: if (selected==33) {castlingBRRmoved = 1;} 395: if (selected==110) {castlingWRLmoved = 1;} 396: if (selected==117) {castlingWRRmoved = 1;} 397: 398: // Now do actual move 399: // Store the content of the square we are moving to 400: test = board[selected+kingmove[i*2+1]]; 401: 402: board[selected+kingmove[i*2+1]] = board[selected]; 403: board[selected] = 0; 404: 405: if (test == 0) 406: { 407: // Turn does NOT end: Ranged movement allowed 408: selected = selected+kingmove[i*2+1]; 409: prevmove = kingmove[i*2+1]; 410: } 411: if (test != 0) 412: { 413: // Standard "end turn" 414: selected = -1; 415: enpassant = -1; 416: if (dRotAuto == 1) {dRot = turn*2;} 417: turn = (turn + 1) % 2; 418: } 419: } 420: label {"move " (selected+kingmove[i*2+1])} 421: } 422: 423: // *** King movement 424: // Normal movement 425: for i ( 0 .. 7 ) 426: { 427: when 428: { 429: (selected != -1) and ((board[selected] + 1) / 2 == 6) and 430: ( 431: (board[selected+kingmove[i]] == 0) or 432: ( 433: (board[selected+kingmove[i]] > 0) and 434: (board[selected+kingmove[i]]%2 == (turn+1)%2) 435: ) 436: ) 437: } 438: allow 439: { 440: // First mark that this movement has disallowed castling 441: if (turn%2==0) {castlingBKmoved = 1;} 442: if (turn%2==1) {castlingWKmoved = 1;} 443: 444: // Now do the actual movement 445: board[selected+kingmove[i]] = board[selected]; 446: board[selected] = 0; 447: // Standard "end turn" 448: selected = -1; 449: enpassant = -1; 450: if (dRotAuto == 1) {dRot = turn*2;} 451: turn = (turn + 1) % 2; 452: } 453: label {"move " (selected+kingmove[i])} 454: } 455: 456: // Castling kingside 457: when 458: { 459: (selected != -1) and ((board[selected] + 1) / 2 == 6) and 460: ( 461: ((turn%2==0) and (castlingBKmoved == 0) and (castlingBRRmoved == 0)) or 462: ((turn%2==1) and (castlingWKmoved == 0) and (castlingWRRmoved == 0)) 463: ) and 464: (board[selected+1] == 0) and (board[selected+2] == 0) 465: } 466: allow 467: { 468: // Disallow further castling 469: if (turn%2==0) {castlingBKmoved = 1;} 470: if (turn%2==1) {castlingWKmoved = 1;} 471: // Do the movement 472: board[selected+2] = board[selected]; 473: board[selected] = 0; 474: board[selected+1] = board[selected+3]; 475: board[selected+3] = 0; 476: // Standard "end turn" 477: selected = -1; 478: enpassant = -1; 479: if (dRotAuto == 1) {dRot = turn*2;} 480: turn = (turn + 1) % 2; 481: } 482: label {"move " (selected+2)} 483: 484: // Castling queenside 485: 486: when 487: { 488: (selected != -1) and ((board[selected] + 1) / 2 == 6) and 489: ( 490: ((turn%2==0) and (castlingBKmoved == 0) and (castlingBRLmoved == 0)) or 491: ((turn%2==1) and (castlingWKmoved == 0) and (castlingWRLmoved == 0)) 492: ) and 493: (board[selected-1] == 0) and (board[selected-2] == 0) and (board[selected-3] == 0) 494: } 495: allow 496: { 497: // Disallow further castling 498: if (turn%2==0) {castlingBKmoved = 1;} 499: if (turn%2==1) {castlingWKmoved = 1;} 500: // Do the movement 501: board[selected-2] = board[selected]; 502: board[selected] = 0; 503: board[selected-1] = board[selected-4]; 504: board[selected-4] = 0; 505: // Standard "end turn" 506: selected = -1; 507: enpassant = -1; 508: if (dRotAuto == 1) {dRot = turn*2;} 509: turn = (turn + 1) % 2; 510: } 511: label {"move " (selected-2)} 512: 513: // *** Continuation of any of the ranged movements 514: when 515: { 516: (selected != -1) and (prevmove != 0) and 517: ( 518: (board[selected+prevmove] == 0) or 519: ( 520: (board[selected+prevmove] > 0) and 521: (board[selected+prevmove]%2 == (turn+1)%2) 522: ) 523: ) 524: } 525: allow 526: { 527: // Store the content of the square we are moving to 528: test = board[selected+prevmove]; 529: 530: board[selected+prevmove] = board[selected]; 531: board[selected] = 0; 532: 533: if (test == 0) 534: { 535: // Ranged movement allowed to continue 536: selected = selected+prevmove; 537: } 538: if (test != 0) 539: { 540: prevmove = 0; 541: // Standard "end turn" 542: selected = -1; 543: enpassant = -1; 544: if (dRotAuto == 1) {dRot = turn*2;} 545: turn = (turn + 1) % 2; 546: } 547: } 548: label {"move " (selected+prevmove)} 549: 550: // *** Allow de-selection of the piece 551: // For a normal piece 552: when {(selected != -1) and (prevmove == 0)} 553: allow {selected = -1;} 554: label {"unselect " selected} 555: 556: // To end a ranged move 557: when {(selected != -1) and (prevmove != 0)} 558: allow 559: { 560: prevmove = 0; 561: // Standard "end turn" 562: selected = -1; 563: enpassant = -1; 564: if (dRotAuto == 1) {dRot = turn*2;} 565: turn = (turn + 1) % 2; 566: } 567: label {"unselect " selected} 568: } 569: 570: display 571: { 572: // White 573: // Pawn - ♙ Knight - ♘ Bishop - ♗ 574: // Rook - ♖ Queen - ♕ King - ♔ 575: // Black 576: // Pawn - ♟ Knight - ♞ Bishop - ♝ 577: // Rook - ♜ Queen - ♛ King - ♚ 578: stringtable counters 579: { 580: " ", 581: "♙", "♟", "♘", "♞", "♗", "♝", 582: "♖", "♜", "♕", "♛", "♔", "♚", 583: " ", 584: "p", "P", "n", "N", "b", "B", 585: "r", "R", "q", "Q", "k", "K" 586: } 587: 588: box {content {"ToothyGDL Chess v0.10"}} 589: 590: newline 591: 592: // Draw the board, assigning all the possible actions as we go 593: // Only render the actual border, not the border spaces 594: for y ( 2 .. (boardheight-3) ) 595: { 596: for x ( 2 .. (boardwidth-3) ) 597: { 598: box 599: { 600: width { 1em } 601: height { 1em } 602: // (dRotA[dRot]+dRotX[dRot]*x+dRotY[dRot]*y) 603: if (board[(dRotA[dRot]+dRotX[dRot]*x+dRotY[dRot]*y)]>=0) 604: {content {counters[ board[(dRotA[dRot]+dRotX[dRot]*x+dRotY[dRot]*y)] + charset*13 ]}} 605: bind_action { "select " (dRotA[dRot]+dRotX[dRot]*x+dRotY[dRot]*y) } 606: bind_action { "unselect " (dRotA[dRot]+dRotX[dRot]*x+dRotY[dRot]*y) } 607: bind_action { "move " (dRotA[dRot]+dRotX[dRot]*x+dRotY[dRot]*y) } 608: 609: // The last border encountered is used 610: border { 2px solid #fff } 611: if (((x+y)%2 == 1) and (board[(dRotA[dRot]+dRotX[dRot]*x+dRotY[dRot]*y)] >= 0)) 612: { border { 2px solid #999 } } 613: while_active { border { 2px solid #00f } } 614: 615: // The last background encountered is used 616: background { #fff } 617: if (((x+y)%2 == 1) and (board[(dRotA[dRot]+dRotX[dRot]*x+dRotY[dRot]*y)] >= 0)) 618: { background { #999 } } 619: } 620: } 621: newline 622: } 623: newline 624: box {height{3px}} 625: newline 626: 627: // Draw the promotion selections if required 628: box {if (promotePiece > -1) {content{"Promote to:  "}}} 629: box 630: { 631: if (promotePiece > -1) 632: { 633: width { 1em } 634: height { 1em } 635: border { 2px solid #00f } 636: background { #999 } 637: content {counters[3 + (1-turn) + charset*13]} 638: bind_action { "promote to knight" } 639: } 640: } 641: box {if (promotePiece > -1) {content{" "}}} 642: box 643: { 644: if (promotePiece > -1) 645: { 646: width { 1em } 647: height { 1em } 648: border { 2px solid #00f } 649: background { #999 } 650: content {counters[5 + (1-turn) + charset*13]} 651: bind_action { "promote to bishop" } 652: } 653: } 654: box {if (promotePiece > -1) {content{" "}}} 655: box 656: { 657: if (promotePiece > -1) 658: { 659: width { 1em } 660: height { 1em } 661: border { 2px solid #00f } 662: background { #999 } 663: content {counters[7 + (1-turn) + charset*13]} 664: bind_action { "promote to rook" } 665: } 666: } 667: box {if (promotePiece > -1) {content{" "}}} 668: box 669: { 670: if (promotePiece > -1) 671: { 672: width { 1em } 673: height { 1em } 674: border { 2px solid #00f } 675: background { #999 } 676: content {counters[9 + (1-turn) + charset*13]} 677: bind_action { "promote to queen" } 678: } 679: } 680: 681: newline 682: box {height{3px}} 683: newline 684: 685: box 686: { 687: content { "      Clockwise      " } 688: bind_action { "rotate clockwise" } 689: border { 1px solid #000 } 690: } 691: box 692: { 693: content { "  Auto-Rotate On  " } 694: if (dRotAuto == 0) { content { "  Auto-Rotate Off  " } } 695: bind_action { "toggle autorotate" } 696: border { 1px solid #000 } 697: } 698: box 699: { 700: content { " Anti-clockwise " } 701: bind_action { "rotate anticlockwise" } 702: border { 1px solid #000 } 703: } 704: 705: newline 706: box {height{3px}} 707: newline 708: 709: stringtable charset_message 710: { 711: "  Switch to ascii pieces  ", 712: " Switch to unicode pieces " 713: } 714: box 715: { 716: content { charset_message[charset] } 717: bind_action { "switch charset" } 718: border { 1px solid #000 } 719: } 720: 721: newline 722: box {content {" "}} 723: }