Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
fiveoutofnine committed Feb 7, 2022
1 parent fe5a941 commit d89e62c
Show file tree
Hide file tree
Showing 10 changed files with 1,653 additions and 16 deletions.
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# fiveoutofnine
**fiveoutofnine** is the first **100% on-chain** chess engine, where minters **play against the smart
contract**. Each move is minted as an NFT, and accompanied by a generative art piece. The majority of
the project's development and beauty is in its algorithms and code that enable such complex
computing, so the art highlights its design and implementation. All metadata and art is generated
and stored 100% on-chain as well (see [`fiveoutofnineART.sol`](https://github.com/fiveoutofnine/fiveoutofnine-chess/tree/main/src/fiveoutofnineART.sol)).

![](sample-art.png)

A live version has been [deployed on mainnet](https://etherscan.io/address/0xb543f9043b387ce5b3d1f0d916e42d8ea2eba2e0#code).

## Installation
To install with [Foundry](https://github.com/gakonst/foundry):

```
forge install fiveoutofnine/fiveoutofnine-chess
```

## Bugs
1. The engine may make a move that leaves its king in check. Pretty minor bug because the game can
progress. Just... without black's king. The fix would be to do 1 more depth of search than the input
depth. Any illegal black moves (moves that result in black's king being checked) would be eliminated
lazily in the extra depth. [<u>Example</u>](https://etherscan.io/tx/0x057b4807662481a3244a25553b52e979c6c5bb3c177344b8585ff17a2a5e0222).

2. The engine evaluates any queen/king move crossing the board's center incorrectly. `toIndex` and
`fromIndex` must be evaluated in separate if/else blocks because they are not related. i.e.

```js
if (fromIndex < 0x12) { // Piece is queen or king and in the closer half
oldPst = (getPstTwo(pieceAtFromIndex) >> (0xC * fromIndex)) & 0xFFF;
newPst = (getPstTwo(pieceAtFromIndex) >> (0xC * toIndex)) & 0xFFF;
} else { // Piece is queen or king and in the further half
oldPst = (getPst(pieceAtFromIndex) >> (0xC * (fromIndex - 0x12))) & 0xFFF;
newPst = (getPst(pieceAtFromIndex) >> (0xC * (toIndex - 0x12))) & 0xFFF;
}
```

should be

```js
if (fromIndex < 0x12) { // Piece is queen or king and moves from the closer half
oldPst = (getPstTwo(pieceAtFromIndex) >> (0xC * fromIndex)) & 0xFFF;
} else { // Piece is queen or king and in the further half
oldPst = (getPst(pieceAtFromIndex) >> (0xC * (fromIndex - 0x12))) & 0xFFF;
}
if (toIndex < 0x12) {
newPst = (getPstTwo(pieceAtFromIndex) >> (0xC * toIndex)) & 0xFFF;
} else {
newPst = (getPst(pieceAtFromIndex) >> (0xC * (toIndex - 0x12))) & 0xFFF;
}
```

There are some minor inaccuracies when the wrong PST is read from fortoIndex. A major inaccuracy
occurs when `toIndex` is greater than `0x12`, it underflows (because it is a `uint256`) and
bitshifts the corresponding PST to `0`. When this happens, any queen/king move is evaluated as a
"very bad" move.
1 change: 1 addition & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@openzeppelin/=lib/openzeppelin-contracts/contracts
Binary file added sample-art.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 43 additions & 0 deletions src/Base64.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

/// @title Base64
/// @author Brecht Devos - <brecht@loopring.org>
/// @notice Provides a function for encoding some bytes in base64
library Base64 {
string internal constant TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678"
"9+/";

function encode(bytes memory data) internal pure returns (string memory) {
if (data.length == 0) return "";
string memory table = TABLE;
uint256 encodedLength = ((data.length + 2) / 3) << 2;
string memory result = new string(encodedLength + 0x20);

assembly {
mstore(result, encodedLength)
let tablePtr := add(table, 1)
let dataPtr := data
let endPtr := add(dataPtr, mload(data))
let resultPtr := add(result, 0x20)
for {} lt(dataPtr, endPtr) {}
{
dataPtr := add(dataPtr, 3)
let input := mload(dataPtr)
mstore(resultPtr, shl(0xF8, mload(add(tablePtr, and(shr(0x12, input), 0x3F)))))
resultPtr := add(resultPtr, 1)
mstore(resultPtr, shl(0xF8, mload(add(tablePtr, and(shr(0xC, input), 0x3F)))))
resultPtr := add(resultPtr, 1)
mstore(resultPtr, shl(0xF8, mload(add(tablePtr, and(shr(6, input), 0x3F)))))
resultPtr := add(resultPtr, 1)
mstore(resultPtr, shl(0xF8, mload(add(tablePtr, and(input, 0x3F)))))
resultPtr := add(resultPtr, 1)
}
switch mod(mload(data), 3)
case 1 { mstore(sub(resultPtr, 2), shl(0xF0, 0x3D3D)) }
case 2 { mstore(sub(resultPtr, 1), shl(0xF8, 0x3D)) }
}

return result;
}
}

0 comments on commit d89e62c

Please sign in to comment.