Numeric and algebraic move notation

Discussion about development of draughts in the time of computer and Internet.
Post Reply
Rein Halbersma
Posts: 1722
Joined: Wed Apr 14, 2004 16:04
Contact:

Numeric and algebraic move notation

Post by Rein Halbersma » Fri Dec 27, 2013 23:19

Here's a piece of C++ code that will allow regular IOstreams output for a move class template. The default is that a move can be output in its native format, i.e. numerical notation for international draughts:

Code: Select all

auto const p = Position<rules::International, board::International>::initial();
auto const moves = successor::generate(p);
std::cout << moves[0]; // print first move as "31-26"
Setting up a different game, e.g. Russian draughts, gives algebraic notation

Code: Select all

auto const p = Position<rules::Russian, board::Checkers>::initial();
auto const moves = successor::generate(p);
std::cout << moves[0]; // print first move as "a3-b4"
Each individual game can have its default move formatting overridden by using so-called manipulators (similar to std::setprecision for formatting numbers in C++).

Code: Select all

auto const p = Position<rules::International, board::International>::initial();
auto const moves = successor::generate(p);
std::cout << format::algebraic << moves[0]; // print first move as "b4-a5"
Here's the C++ code (using C++14 automatic return types, and the little-known feature of IOstreams that allows users to add "sticky" flags that control formatting of user-defined classes. These flags can be modified/accessed by the iword() library function with a pre-computed index obtained from the xalloc() library function):
Spoiler:

Code: Select all

namespace format {

enum { flag_native = 0, flag_numeric = 1, flag_algebraic = 2 };

template<class Move>
struct traits
:
        std::integral_constant<int, flag_numeric>
{};

template<class Board>
struct traits<Move<rules::Russian, Board>>
:
        std::integral_constant<int, flag_algebraic>
{};

template<class Board>
struct traits<Move<rules::Czech, Board>>
:
        std::integral_constant<int, flag_algebraic>
{};

template<>
struct traits<Move<rules::International, board::Checkers>>
:
        std::integral_constant<int, flag_algebraic>
{};

inline
auto index()
{
        static auto const slot = std::ios_base::xalloc();
        return slot;
}

template<class CharT, class Traits>
auto& numeric(std::basic_ostream<CharT, Traits>& ostr)
{
        ostr.iword(index()) = flag_numeric;
        return ostr;
}

template<class CharT, class Traits>
auto& algebraic(std::basic_ostream<CharT, Traits>& ostr)
{
        ostr.iword(index()) = flag_algebraic;
        return ostr;
}

template<class Move>
auto as_numeric(Move const& m)
{
        using Board = typename Move::board_type;
        std::stringstream sstr;
        sstr << Board::numeric_from_bit(m.from());
        sstr << (m.is_jump() ? 'x' : '-');
        sstr << Board::numeric_from_bit(m.dest());
        return sstr.str();
}

template<class Move>
auto as_algebraic(Move const& m)
{
        using Board = typename Move::board_type;
        std::stringstream sstr;
        sstr << Board::algebraic_from_bit(m.from());
        sstr << (m.is_jump() ? 'x' : '-');
        sstr << Board::algebraic_from_bit(m.dest());
        return sstr.str();
}

}       // namespace format

template<class Rules, class Board>
auto& operator<<(std::ostream& ostr, Move<Rules, Board> const& m)
{
        auto value = ostr.iword(format::index());
        if (value == format::flag_native)
                value = format::traits<Move<Rules, Board>>::value;
        switch(value) {
        case format::flag_numeric:
                ostr << format::as_numeric(m);
                break;
        case format::flag_algebraic:
                ostr << format::as_algebraic(m);
                break;
        default:
                assert(false && !"Supplied move format not supported.");
                break;
        }
        return ostr;
}
Many thanks to Wieger Wesselink for showing me his small interactive program that used the plain overloaded operator<< for moves. Being able to customized the move format without resorting to my old verbose notation::write<SomeFormatFlag>(m) was a nice exercise!

Post Reply