Coder Social home page Coder Social logo

chess's People

Contributors

nmlorg avatar

Watchers

 avatar

chess's Issues

Convert [x, y] to 'lldd' notation

In an 8×8 grid, locations are named as a letter from a–h for the column and a digit from 1–8 for the row:

a8 b8 c8 d8 e8 f8 g8 h8
a7 b7 c7 d7 e7 f7 g7 h7
a6 b6 c6 d6 e6 f6 g6 h6
a5 b5 c5 d5 e5 f5 g5 h5
a4 b4 c4 d4 e4 f4 g4 h4
a3 b3 c3 d3 e3 f3 g3 h3
a2 b2 c2 d2 e2 f2 g2 h2
a1 b1 c1 d1 e1 f1 g1 h1

This can be extended to support up to 26×∞ grids by using a–z followed by one or more digits. To go beyond 26 columns, borrowing from spreadsheets, we can use aa, ab, …, az, ba, …, zz, …, aaa, ….

To simplify, using the letters a–c:

num str literal expr
0 a ('a' + 0) ('a' + num)
1 b ('a' + 1) ('a' + num)
2 c ('a' + 2) ('a' + num)
3 aa ('a' + 0) ('a' + 0) ('a' + num // 3 - 1) ('a' + num % 3)
4 ab ('a' + 0) ('a' + 1) ('a' + num // 3 - 1) ('a' + num % 3)
5 ac ('a' + 0) ('a' + 2) ('a' + num // 3 - 1) ('a' + num % 3)
6 ba ('a' + 1) ('a' + 0) ('a' + num // 3 - 1) ('a' + num % 3)
7 bb ('a' + 1) ('a' + 1) ('a' + num // 3 - 1) ('a' + num % 3)
8 bc ('a' + 1) ('a' + 2) ('a' + num // 3 - 1) ('a' + num % 3)
9 ca ('a' + 2) ('a' + 0) ('a' + num // 3 - 1) ('a' + num % 3)
10 cb ('a' + 2) ('a' + 1) ('a' + num // 3 - 1) ('a' + num % 3)
11 cc ('a' + 2) ('a' + 2) ('a' + num // 3 - 1) ('a' + num % 3)
12 aaa ('a' + 0) ('a' + 0) ('a' + 0) ('a' + num // 9 - 1) ('a' + (num // 3) % 3 - 1) ('a' + num % 3)
13 aab ('a' + 0) ('a' + 0) ('a' + 1) ('a' + num // 9 - 1) ('a' + (num // 3) % 3 - 1) ('a' + num % 3)

Rework Board.legalmoves

Right now, each piece calculates its legal moves on the fly (see Bishop.legalmoves(), etc.).

Separately, Board.legalmoves() collects the legal moves of all of its pieces, but isn't actually used anywhere.

In the HTML app, every time a piece is moved, ChessBoardElement.update() calls every square's ChessSquareElement.update(), which calls Piece.legalmoves() (Bishop.legalmoves(), etc.) and then does some postprocessing to both record when a target square is under attack and to reproduce Board.legalmoves()' current-player-only behavior.

I'm thinking there should be a new Move type which stores everything about a particular move, including:

  • source/target Square pairs (>1 during castling)
  • new Board

as well as pull some of the logic from the HTML app to collect information about squares that are under attack, etc. into Board.

It might make sense to have the full set of a Board's legal Moves be computed once up front—likely at the end of Board.load() and Board.move(), though it might also make sense to go back to having Board.move() return a new Board, or even do away with Board.move() entirely in favor of strictly transitioning using game.board = move.board or game.board.load(move.board.serialize()).

Assertions as statements

Right now, the test runner passes a function into each test as U.assert, then tests use it like:

chess/test_test.js

Lines 21 to 23 in 682e55a

export function testAssertExpectedToFail(U) {
U.assert(1 == 2);
}

When an assertion fails, it shows up like:

test_test.js:testAssertExpectedToFail FAIL
Error: Assertion failed
    at Object.assert (https://nmlorg.github.io/chess/test.html:24:9)
    at testAssertExpectedToFail (https://nmlorg.github.io/chess/test_test.js:22:5)
    at https://nmlorg.github.io/chess/test.html:38:9

with no reference to what exactly failed (the user needs to manually find the "test_test.js:22", open test_test.js, then jump to line 22).

I would like to add a new assert statement, which captures not just the expression being tested, but the values of each component of the expression; so:

  assert 1 == 2;

would show up as something like:

Error: Assertion failed: 1 == 2
    at testAssertExpectedToFail (https://nmlorg.github.io/chess/test_test.js:22:5)

and:

  assert Position.fromXY(0, 0) == 'xx';

might show up something like:

Error: Assertion failed: Position.fromXY(0, 0) ['a1'] == 'xx'
    at test_Position_fromXY (...)

or even:

Error: Assertion failed:
      Position.fromXY(0, 0) == 'xx'
        Position.fromXY(0, 0) = 'a1'
    at test_Position_fromXY (...)

to support more complicated things like:

  assert board.get('a1').piece.constructor.name == 'xxx';
  assert board.get('a2').piece.constructor.name == 'yyy';

becoming:

Error: Assertion failed:
      board.get('a1').piece.constructor.name == 'xxx'
        board instanceof Board
          board.get('a1') instanceof Square
            board.get('a1').piece instanceof Pawn
              board.get('a1').piece.constructor = Pawn
                board.get('a1').piece.constructor.name = 'Pawn'
Error: Assertion failed:
      board.get('a2').piece.constructor.name == 'yyy'
        board instanceof Board
          board.get('a2') instanceof Square
            board.get('a2').piece = undefined

Square.attackable

The various Piece.legalmoves() functions repeat checking if a potential move attacks an enemy a lot. This can't really be done in a postprocessing step because, for example, when a bishop moves, it stops once it hits the first unattackable square in a given diagonal, but a post-processing step that just either removes un-attackable squares or everything after the first un-attackable square will remove too few or too many squares.

It might make sense to pull this into Square—or possibly something that wraps Square on the fly. In both the current checked-in code and a currently unpushed rewrite, Square.up, etc., are computed during Board initialization. I'm thinking maybe moving those into Square._up or .literal_up and having .up be a getter that returns something like SquareWrapper(this, this._up) which just introduces attributes relative to the original Square and proxies the rest along.

That is, right now, a board serialized as p\np\nP would have board.get('a1').up == board.get('a2') == board.get('a3').down. I would change it so board.get('a1').up == SquareWrapper(board.get('a1'), board.get('a2')) and board.get('a3').up == SquareWrapper(board.get('a3'), board.get('a2')), and the two wrappers would evaluate .attackable (etc.(?)) based on the piece in the first argument. (So a2 as seen by a1 would be attackable, but the same square as seen by a3 would not, because it has a piece owned by the same player.)

New serialization

In addition to supporting FEN/X-FEN, I want to simplify the visual serialization format. Right now, every piece's move count is stored, which can (or at least, I believe, should) be able to be used to support both en passant and castling availability (as well as calculating the fullmove number). However—inspired by FEN—I would like to simplify this to just storing explicit en passant and castling information. Rather than storing them separately from the board, however, I'm thinking I might be able to introduce two new characters, so an en passant move can be represented by perhaps x and a castle-able rook can perhaps be represented by c/C. The standard starting layout would change from:

r n b q k b n r
p p p p p p p p
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
P P P P P P P P
R N B Q K B N R

to:

c n b q k b n c
p p p p p p p p
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
P P P P P P P P
C N B Q K B N C

and the move 1.a4 would change from:

r  n  b  q  k  b  n  r
p  p  p  p  p  p  p  p
.  .  .  .  .  .  .  .
.  .  .  .  .  .  .  .
P1 .  .  .  .  .  .  .
.  .  .  .  .  .  .  .
.  P  P  P  P  P  P  P
R  N  B  Q  K  B  N  R

to:

c n b q k b n c
p p p p p p p p
. . . . . . . .
. . . . . . . .
P . . . . . . .
x . . . . . . .
. P P P P P P P
C N B Q K B N C

(so, among other things, the exact dimensions of the grid will always be the same/there will always be one space between pieces).

The game 1.a4 a5 2.a2 a7 3.a1 a8 would be:

c n b q k b n c    c n b q k b n c    c n b q k b n c    . n b q k b n c    . n b q k b n c    r n b q k b n c
p p p p p p p p    . p p p p p p p    . p p p p p p p    r p p p p p p p    r p p p p p p p    . p p p p p p p
. . . . . . . .    x . . . . . . .    . . . . . . . .    . . . . . . . .    . . . . . . . .    . . . . . . . .
. . . . . . . .    p . . . . . . .    p . . . . . . .    p . . . . . . .    p . . . . . . .    p . . . . . . .
P . . . . . . .    P . . . . . . .    P . . . . . . .    P . . . . . . .    P . . . . . . .    P . . . . . . .
x . . . . . . .    . . . . . . . .    . . . . . . . .    . . . . . . . .    . . . . . . . .    . . . . . . . .
. P P P P P P P    . P P P P P P P    R P P P P P P P    R P P P P P P P    . P P P P P P P    . P P P P P P P
C N B Q K B N C    C N B Q K B N C    . N B Q K B N C    . N B Q K B N C    R N B Q K B N C    R N B Q K B N C

(showing that, even though the queen-side rooks are [back] in their castle-able positions, they've lost their castle-ability).

Note that if the king moves it can no longer castle (and so neither can either of its rooks). I would like to keep castle-ability strictly relative to the rook, but that would require marking it on the king somehow. I considered using a separate marker, showing either castle-ability or castle-inability, like Rc N  B  Q  Kc B  N  Rc, but I thought it would be tedious to keep using the marker either from the very beginning or once either a king or rook had moved.

This also would make the board not strictly serializable using Unicode characters (there is no code point for a castle-able rook, nor an en passant space; however, there are symbols for things like U+1FA0B WHITE CHESS ROOK ROTATED NINETY DEGREES that could represent "tapped" pieces).

Also note that pawns promoted to rooks would start off castle-unable.

I am not sure if/how to represent the fullmove or halfmove numbers. It might end up making the most sense to keep these as attributes of a Game rather than a Board (so exporting/importing using FEN would be implemented by the Game object), since they don't actually affect what moves are valid from a given board layout.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.