Add legal chess captures exercise

This commit is contained in:
Camden Dixie O'Brien 2023-12-13 16:36:46 +00:00
parent ec9d9b2a38
commit 5f57627225

340
20_legal_captures.lua Normal file
View File

@ -0,0 +1,340 @@
-- Write a function which takes a description of a chess position and
-- returns a list of all legal captures from that position.
--
-- The position will be given as a table, containing the keys "turn",
-- indicating which player it is to move, "board", which will contain
-- a rank-major grid of pieces (that is, a list of ranks/rows). Each
-- piece will be indicated by its unicode symbol. The lack of a piece
-- will be indicated by nil. If the previous move was a pawn moving
-- two places, the key "en_passant_target" will be set to a table with
-- "rank" and "file" keys set to the (1-based) rank and file indices
-- of the square passed over by the pawn.
--
-- For example, the starting position would be given as:
--
-- {
-- turn = "white",
-- board = {
-- {"♖", "♘", "♗", "♕", "♔", "♗", "♘", "♖"},
-- {"♙", "♙", "♙", "♙", "♙", "♙", "♙", "♙"},
-- {nil, nil, nil, nil, nil, nil, nil, nil},
-- {nil, nil, nil, nil, nil, nil, nil, nil},
-- {nil, nil, nil, nil, nil, nil, nil, nil},
-- {nil, nil, nil, nil, nil, nil, nil, nil},
-- {"♟︎", "♟︎", "♟︎", "♟︎", "♟︎", "♟︎", "♟︎", "♟︎"},
-- {"♜", "♞", "♝", "♛", "♚", "♝", "♞", "♜"},
-- },
-- }
--
-- Moves should be returned in figurine algebraic notation, without
-- indicators for en passant, check or checkmate (i.e. no "e.p.", "+"
-- or "#"). For example, "♞xc6" for a black night capturing on c6 and
-- "exd7" for an e-file pawn capturing on d7. For the sake of
-- simplicity, there's no requirement to disambiguate between two
-- pieces of the same type when the notation for the capture would
-- otherwise be the same (e.g. if two rooks can capture on the same
-- square). However, there should be duplicate entries in the results
-- list for each.
--
-- Solution --------------------------------------------------------------------
function legal_captures(position)
-- Your implementation here
end
-- Tests -----------------------------------------------------------------------
local luaunit = require("luaunit.luaunit")
function test_starting_position()
local position = {
turn = "white",
board = {
{"", "", "", "", "", "", "", ""},
{"", "", "", "", "", "", "", ""},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{"♟︎", "♟︎", "♟︎", "♟︎", "♟︎", "♟︎", "♟︎", "♟︎"},
{"", "", "", "", "", "", "", ""},
},
}
local expected = {}
luaunit.assertItemsEquals(legal_captures(position), expected)
end
function test_queens_gambit()
local position = {
turn = "black",
board = {
{"", "", "", "", "", "", "", ""},
{"", "", nil, nil, "", "", "", ""},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, "", "", nil, nil, nil, nil},
{nil, nil, nil, "♟︎", nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{"♟︎", "♟︎", "♟︎", nil, "♟︎", "♟︎", "♟︎", "♟︎"},
{"", "", "", "", "", "", "", ""},
},
}
local expected = {"dxc4"}
luaunit.assertItemsEquals(legal_captures(position), expected)
end
function test_scandinavian_defense()
local position = {
turn = "white",
board = {
{"", "", "", "", "", "", "", ""},
{"", "", "", "", nil, "", "", ""},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, "", nil, nil, nil},
{nil, nil, nil, "♟︎", nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{"♟︎", "♟︎", "♟︎", nil, "♟︎", "♟︎", "♟︎", "♟︎"},
{"", "", "", "", "", "", "", ""},
},
}
local expected = {"exd5"}
luaunit.assertItemsEquals(legal_captures(position), expected)
end
function test_knight_captures()
local position = {
turn = "black",
board = {
{"", "", "", "", "", "", "", ""},
{"", "", "", nil, "", "", "", ""},
{"", "", nil, nil, nil, "", "", ""},
{"", nil, nil, "", nil, nil, "", ""},
{"", "", nil, nil, nil, "", "", ""},
{"", "", "", nil, "", "", "", ""},
{"", "", "", "", "", "", "", ""},
{"", "", "", "", "", "", "", ""},
},
}
local expected = {
"♞xb3", "♞xb5", "♞xc2", "♞xc6", "♞xe2", "♞xe6", "♞xf3", "♞xf5",
}
luaunit.assertItemsEquals(legal_captures(position), expected)
end
function test_knight_not_blocked()
local position = {
turn = "white",
board = {
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, "♟︎", "♟︎", "♟︎", nil, nil},
{nil, nil, nil, nil, "♟︎", nil, nil, nil},
{nil, nil, "♟︎", "♟︎", "", nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
},
}
local expected = {"♘xd3", "♘xf3"}
luaunit.assertItemsEquals(legal_captures(position), expected)
end
function test_bishop_captures()
local position = {
turn = "black",
board = {
{nil, nil, nil, nil, nil, nil, nil, nil},
{"", nil, nil, nil, nil, nil, "", nil},
{nil, nil, nil, "", nil, nil, "", nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, "", "", nil, nil, nil, ""},
{nil, nil, nil, nil, "", nil, nil, nil},
{nil, "", nil, nil, nil, nil, nil, nil},
{nil, nil, nil, "", nil, nil, nil, nil},
},
}
local expected = {"♝xa2", "♝xb7", "♝xe6", "♝xg2"}
luaunit.assertItemsEquals(legal_captures(position), expected)
end
function test_bishop_blocked()
local position = {
turn = "black",
board = {
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, "", nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, "", nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, "", nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
},
}
local expected = {}
luaunit.assertItemsEquals(legal_captures(position), expected)
end
function test_rook_captures()
local position = {
turn = "white",
board = {
{nil, nil, nil, nil, nil, nil, nil, nil},
{"♟︎", nil, nil, nil, nil, nil, "♟︎", nil},
{nil, nil, nil, "♟︎", nil, nil, "♟︎", nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, "♟︎", "", nil, nil, nil, "♟︎"},
{nil, nil, nil, nil, "♟︎", nil, nil, nil},
{nil, "♟︎", nil, nil, nil, nil, nil, nil},
{nil, nil, nil, "♟︎", nil, nil, nil, nil},
},
}
local expected = {"♖xc5", "♖xd3", "♖xd8", "♖xh5"}
luaunit.assertItemsEquals(legal_captures(position), expected)
end
function test_rook_blocked()
local position = {
turn = "white",
board = {
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, "", "", nil, nil, nil, "♟︎", nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
},
}
local expected = {}
luaunit.assertItemsEquals(legal_captures(position), expected)
end
function test_queen_captures()
local position = {
turn = "black",
board = {
{nil, "", "", "", "", "", nil, ""},
{"", "", "", nil, "", "", "", ""},
{"", "", nil, nil, nil, "", "", ""},
{"", nil, nil, "", nil, nil, "", nil},
{"", "", nil, nil, nil, "", "", ""},
{"", "", "", nil, "", "", "", ""},
{nil, "", "", "", "", "", nil, ""},
{"", "", "", nil, "", "", "", nil},
},
}
local expected = {
"♛xa4", "♛xb2", "♛xb6", "♛xd1", "♛xd7", "♛xf2", "♛xf6", "♛xg4",
}
luaunit.assertItemsEquals(legal_captures(position), expected)
end
function test_queen_blocked()
local position = {
turn = "white",
board = {
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, "♟︎", nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, "", nil, nil, nil, nil, nil},
{nil, "", "", nil, nil, nil, "♟︎", nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
},
}
local expected = {}
luaunit.assertItemsEquals(legal_captures(position), expected)
end
function test_king_captures()
local position = {
turn = "black",
board = {
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, "", "", "", nil, nil, nil},
{nil, nil, "", "", "", nil, nil, nil},
{nil, nil, "", "", "", nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
},
}
local expected = {"♚xc3","♚xc4","♚xc5","♚xd3","♚xd5","♚xe3","♚xe4","♚xe5",}
luaunit.assertItemsEquals(legal_captures(position), expected)
end
function test_en_passant()
local position = {
turn = "white",
board = {
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, "♟︎", "", nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
},
en_passant_target = {rank = 6, file = 4},
}
local expected = {"exd6"}
luaunit.assertItemsEquals(legal_captures(position), expected)
end
function test_cannot_capture_if_leads_to_check()
local position = {
turn = "white",
board = {
{nil, nil, nil, nil, "", nil, nil, nil},
{nil, nil, nil, nil, "", nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, "♟︎", nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, "", nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
},
en_passant_target = {rank = 6, file = 4},
}
local expected = {}
luaunit.assertItemsEquals(legal_captures(position), expected)
end
function test_position_with_lots_of_captures()
local position = {
turn = "white",
board = {
{"", nil, nil, "", "", nil, nil, ""},
{nil, "", "", "", "", "", "", nil},
{nil, nil, nil, nil, nil, nil, nil, nil},
{"", "", "", "", "", "", "", ""},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, "", "", "", "", "", "", ""},
{nil, nil, nil, nil, nil, nil, nil, nil},
{nil, nil, nil, "", "", "", nil, nil},
},
en_passant_target = {rank = 6, file = 4},
}
local expected = {
"♔xe1", "♔xe2", "♔xc2", "♖xh4", "♖xe1",
"♖xb4", "♖xc2", "♕xf4", "♕xb4", "♕xe1",
"♕xd4", "♕xe2", "♕xc2", "♕xh4", "♕xd4",
"♕xe1", "♕xf4", "♕xe2", "♘xh4", "♘xf4",
"♘xe1", "♗xc6", "♗xc2", "♕xe6", "♕xe2",
"♕xc6", "♕xc2", "♕xd4", "♕xb4", "♕xc6",
"♕xc2", "♕xe6", "♕xe2", "♕xf4", "♕xd4",
"♕xe6", "♕xe2", "♕xh4", "♕xf4", "♕xd8",
"♕xd4", "♕xb4", "♕xc6", "♕xf8", "♕xf4",
"♕xb4", "♕xd8", "♕xd4", "♕xe6", "♕xc6",
"♕xd8", "♕xh4", "♕xd4", "♕xf8", "♕xf4",
"♕xe6", "♘xf8", "♘xh4", "♘xf4", "♗xf8",
"♗xf4", "♕xc6", "♕xe6", "♕xf8", "♕xd8",
}
local captures = legal_captures(position)
luaunit.assertItemsEquals(captures, expected)
end
os.exit(luaunit.LuaUnit.run())