Skip to main content
deleted 33 characters in body
Source Link
# lowercase == white, uppercase == black
# p == pawn
# n == kNight
# b == Bishop
# r == Rook
# q == Queen
# k == King
# . == empty space
BASE_CHESS_LOOKUP_TABLE = [".","p","n","b","r","q","k","P","N","B","R","Q","K"]

#message between 26 and 32 chars

# ENCODE LOGIC
def _encode_ingest(c: str, _previous_state: str = "") -> str:
    """
    Ingests a single char, + previous state
    Outputs a state to be `encode_digest`ed at the end. 
    """
    assert(len(c) == 1)
    o = ord(c.encode('ascii'))
    assert(o < 128)
    return _previous_state + bin(o)[2:].zfill(7)

def _encode_digest(final_state: str) -> str:
    """
    Digests a state computed from `encode_ingest`, outputs a chess-encoded string.
    """
    res = ""
    i = 0
    while i < len(final_state):
        jump = 4
        if i+3 >= len(final_state):
            #need to make sure up to 3 bits are used for encoding completion
            final_state += "0"*(3-(len(final_state)-i))
            res += BASE_CHESS_LOOKUP_TABLE[int(final_state[i:len(final_state)], 2)]
        else:
            index = int(final_state[i:i+4], 2)
            if index > 7 and index < len(BASE_CHESS_LOOKUP_TABLE): # if it can hold 4 bits, do it
                res += BASE_CHESS_LOOKUP_TABLE[index]
            else:
                res += BASE_CHESS_LOOKUP_TABLE[int(final_state[i:i+3], 2)]
                jump = 3
        i += jump

    # fill empty spaces in the board
    res += "."*(64-len(res))
    if len(res) > 64: raise ValueError("Message too large")
    return res

def encode_chess(s: str) -> str:
    """
    Encodes the given string into another 'chess-encoded' string.
    String length is up to 26 reliably, but may go up to 32 with good bit alignment.
    """
    state = ""
    for c in s:
        state = _encode_ingest(c, state)
    return _encode_digest(state)
# ENCODE LOGIC END

# DECODE LOGIC
def _decode_ingest(p: str, _previous_state: str = "") -> str:
    """
    Ingests a single 'piece' of chess (str), + previous state
    Outputs a state to be decoded with `decode_digests`. 
    """
    return _previous_state + bin(BASE_CHESS_LOOKUP_TABLE.index(p))[2:].zfill(3)

def _decode_digest(final_state: str) -> str:
    """
    Digests a state computed from `decode_ingest`, outputs a chess-decoded string.
    """
    res = ""
    last = 0
    for i in range(7,len(final_state), 7):
        res += chr(int(final_state[last:i], 2))
        last = i
    if last < len(final_state):
        res += chr(int(final_state[last:len(final_state)], 2))

    return res.rstrip('\x00') # Right-most NUL are just empty spaces on the board, not from the message

def decode_chess(chess: str) -> str:
    """
    Decodes a 'chess-encoded' string into the original human-readable string.
    Encoded string must be exactly 64 characters.
    """
    assert(len(chess) == 64)
    state = ""
    for piece in chess:
        state = _decode_ingest(piece, state)
    return _decode_digest(state)
# DECODE LOGIC END
# lowercase == white, uppercase == black
# p == pawn
# n == kNight
# b == Bishop
# r == Rook
# q == Queen
# k == King
# . == empty space
BASE_CHESS_LOOKUP_TABLE = [".","p","n","b","r","q","k","P","N","B","R","Q","K"]

#message between 26 and 32 chars

# ENCODE LOGIC
def _encode_ingest(c: str, _previous_state: str = "") -> str:
    """
    Ingests a single char, + previous state
    Outputs a state to be `encode_digest`ed at the end. 
    """
    assert(len(c) == 1)
    o = ord(c.encode('ascii'))
    assert(o < 128)
    return _previous_state + bin(o)[2:].zfill(7)

def _encode_digest(final_state: str) -> str:
    """
    Digests a state computed from `encode_ingest`, outputs a chess-encoded string.
    """
    res = ""
    i = 0
    while i < len(final_state):
        jump = 4
        if i+3 >= len(final_state):
            #need to make sure up to 3 bits are used for encoding completion
            final_state += "0"*(3-(len(final_state)-i))
            res += BASE_CHESS_LOOKUP_TABLE[int(final_state[i:len(final_state)], 2)]
        else:
            index = int(final_state[i:i+4], 2)
            if index > 7 and index < len(BASE_CHESS_LOOKUP_TABLE): # if it can hold 4 bits, do it
                res += BASE_CHESS_LOOKUP_TABLE[index]
            else:
                res += BASE_CHESS_LOOKUP_TABLE[int(final_state[i:i+3], 2)]
                jump = 3
        i += jump

    # fill empty spaces in the board
    res += "."*(64-len(res))
    if len(res) > 64: raise ValueError("Message too large")
    return res

def encode_chess(s: str) -> str:
    """
    Encodes the given string into another 'chess-encoded' string.
    String length is up to 26 reliably, but may go up to 32 with good bit alignment.
    """
    state = ""
    for c in s:
        state = _encode_ingest(c, state)
    return _encode_digest(state)
# ENCODE LOGIC END

# DECODE LOGIC
def _decode_ingest(p: str, _previous_state: str = "") -> str:
    """
    Ingests a single 'piece' of chess (str), + previous state
    Outputs a state to be decoded with `decode_digests`. 
    """
    return _previous_state + bin(BASE_CHESS_LOOKUP_TABLE.index(p))[2:].zfill(3)

def _decode_digest(final_state: str) -> str:
    """
    Digests a state computed from `decode_ingest`, outputs a chess-decoded string.
    """
    res = ""
    last = 0
    for i in range(7,len(final_state), 7):
        res += chr(int(final_state[last:i], 2))
        last = i
    if last < len(final_state):
        res += chr(int(final_state[last:len(final_state)], 2))

    return res.rstrip('\x00') # Right-most NUL are just empty spaces on the board, not from the message

def decode_chess(chess: str) -> str:
    """
    Decodes a 'chess-encoded' string into the original human-readable string.
    Encoded string must be exactly 64 characters.
    """
    assert(len(chess) == 64)
    state = ""
    for piece in chess:
        state = _decode_ingest(piece, state)
    return _decode_digest(state)
# DECODE LOGIC END
# lowercase == white, uppercase == black
# p == pawn
# n == kNight
# b == Bishop
# r == Rook
# q == Queen
# k == King
# . == empty space
BASE_CHESS_LOOKUP_TABLE = [".","p","n","b","r","q","k","P","N","B","R","Q","K"]


# ENCODE LOGIC
def _encode_ingest(c: str, _previous_state: str = "") -> str:
    """
    Ingests a single char, + previous state
    Outputs a state to be `encode_digest`ed at the end. 
    """
    assert(len(c) == 1)
    o = ord(c.encode('ascii'))
    assert(o < 128)
    return _previous_state + bin(o)[2:].zfill(7)

def _encode_digest(final_state: str) -> str:
    """
    Digests a state computed from `encode_ingest`, outputs a chess-encoded string.
    """
    res = ""
    i = 0
    while i < len(final_state):
        jump = 4
        if i+3 >= len(final_state):
            #need to make sure up to 3 bits are used for encoding completion
            final_state += "0"*(3-(len(final_state)-i))
            res += BASE_CHESS_LOOKUP_TABLE[int(final_state[i:len(final_state)], 2)]
        else:
            index = int(final_state[i:i+4], 2)
            if index > 7 and index < len(BASE_CHESS_LOOKUP_TABLE): # if it can hold 4 bits, do it
                res += BASE_CHESS_LOOKUP_TABLE[index]
            else:
                res += BASE_CHESS_LOOKUP_TABLE[int(final_state[i:i+3], 2)]
                jump = 3
        i += jump

    # fill empty spaces in the board
    res += "."*(64-len(res))
    if len(res) > 64: raise ValueError("Message too large")
    return res

def encode_chess(s: str) -> str:
    """
    Encodes the given string into another 'chess-encoded' string.
    String length is up to 26 reliably, but may go up to 32 with good bit alignment.
    """
    state = ""
    for c in s:
        state = _encode_ingest(c, state)
    return _encode_digest(state)
# ENCODE LOGIC END

# DECODE LOGIC
def _decode_ingest(p: str, _previous_state: str = "") -> str:
    """
    Ingests a single 'piece' of chess (str), + previous state
    Outputs a state to be decoded with `decode_digests`. 
    """
    return _previous_state + bin(BASE_CHESS_LOOKUP_TABLE.index(p))[2:].zfill(3)

def _decode_digest(final_state: str) -> str:
    """
    Digests a state computed from `decode_ingest`, outputs a chess-decoded string.
    """
    res = ""
    last = 0
    for i in range(7,len(final_state), 7):
        res += chr(int(final_state[last:i], 2))
        last = i
    if last < len(final_state):
        res += chr(int(final_state[last:len(final_state)], 2))

    return res.rstrip('\x00') # Right-most NUL are just empty spaces on the board, not from the message

def decode_chess(chess: str) -> str:
    """
    Decodes a 'chess-encoded' string into the original human-readable string.
    Encoded string must be exactly 64 characters.
    """
    assert(len(chess) == 64)
    state = ""
    for piece in chess:
        state = _decode_ingest(piece, state)
    return _decode_digest(state)
# DECODE LOGIC END
added 1 character in body
Source Link

Without these improvements, one would need 3 pieces (4,4,2 bits) to encode a single byte, leaving a theoretical maximum of 64/3 = 21 characters . But with the improvements, the cipher needs as little as 2 pieces (4,3 bits), or 3 pieces but sharing space with the next character (43,3,21 + 1 bit2 bits from next). This allows a theoretical maximum of 64/2 = 32 characters using 4+3 bit pieces, and potentially using 4+3(+1 from the next) bits = 36 characters. Though admittedly, reaching this upper limit of 36 is not viable for arbitrary messages.

Without these improvements, one would need 3 pieces (4,4,2 bits) to encode a single byte, leaving a theoretical maximum of 64/3 = 21 characters . But with the improvements, the cipher needs as little as 2 pieces (4,3 bits), or 3 pieces but sharing space with the next character (4,3,2 + 1 bit from next). This allows a theoretical maximum of 64/2 = 32 characters using 4+3 bit pieces, and potentially using 4+3(+1 from the next) bits = 36 characters. Though admittedly, reaching this upper limit of 36 is not viable for arbitrary messages.

Without these improvements, one would need 3 pieces (4,4,2 bits) to encode a single byte, leaving a theoretical maximum of 64/3 = 21 characters . But with the improvements, the cipher needs as little as 2 pieces (4,3 bits), or 3 pieces but sharing space with the next character (3,3,1 + 2 bits from next). This allows a theoretical maximum of 64/2 = 32 characters using 4+3 bit pieces, and potentially using 4+3(+1 from the next) bits = 36 characters. Though admittedly, reaching this upper limit of 36 is not viable for arbitrary messages.

added 88 characters in body
Source Link

BaseChess translates arbitrary text (see below) into a (very probably game-invalid) chess board state. It can fit ~30 characters reliably in one board (see below).

  • Used chess. Because I like it.

  • Is less than 100 lines long. Because I'm horrible with feature creep.

  • Could be feasibly done by a human. Not a competition requirement, but i wanted to anyways.

  • Is versatile enough to fit almost any (short) English message.

  • Is as space efficient as possible. I paid for 64 squares and I'm going to use them!

  • Used no libraries.

BaseChess translates arbitrary text (see below) into a (very probably game-invalid) chess board state.

  • Used chess. Because I like it.

  • Is less than 100 lines long. Because I'm horrible with feature creep.

  • Could be feasibly done by a human. Not a competition requirement, but i wanted to anyways.

  • Is versatile enough to fit almost any (short) English message.

  • Is as space efficient as possible. I paid for 64 squares and I'm going to use them!

BaseChess translates arbitrary text (see below) into a (very probably game-invalid) chess board state. It can fit ~30 characters reliably in one board (see below).

  • Used chess. Because I like it.

  • Is less than 100 lines long. Because I'm horrible with feature creep.

  • Could be feasibly done by a human. Not a competition requirement, but i wanted to anyways.

  • Is versatile enough to fit almost any (short) English message.

  • Is as space efficient as possible. I paid for 64 squares and I'm going to use them!

  • Used no libraries.

Source Link
Loading