Interface Extension

I’ve been programming an alpha-beta AI to play variants of connect-k games. For example, connect-3 on a 3×3 board with gravity off is tic-tac-toe, connect-5 on a 19×19 board with gravity off is the basis of go-moku, while connect-4 on a 7×6 board with gravity on is connect four.

Along the way, I’ve made myself a class to store a board position. This is a simple class consisting of two int‘s. I want to use int because it can store -1 which is clearly an invalid position. I can then make a convenient method bool isValid(); that can be used to query the position to see if it lies on the board. But, what about positions that are beyond the board size? The isValid() method should clearly say false if it does not lie on the board, yet the position class shouldn’t have knowledge about the board size!

Given the limitation of C++ (my implementation language of choice), I’ve given the board a method for testing the validity of a point: bool isValid(Position const& pos) const. Semantically though, I’d rather say

pos.isValid();

over

m_board->isValid(pos);

So, what do I really want? I want interface extensions! Within the board class, I would like to extend the methods that are available on the point class. I’m willing to limit the implementation of such methods to using all items within scope at the site of implementation, and all publicly visible methods on point itself. The plan is to allow something like this:

class Board {
    ...
    void move(Player player, Point const &pos) {
       ASSERT(pos.isValid());
       ...
    }
 
    func (Point p) isValid() bool {
        return 0 < p.x() && p.x() < this->numCols
             && 0 < p.y() && p.y() < this->numRows;
    }
}

The declaration of isValid() is not valid C++. I’ve borrowed both the syntax and idea of interface extension from Google’s go. The this pointer is, of course, implicit for all methods declared in the Board class, ala C++ rules. What’s funky though, is that the isValid() method now has two first arguments! When writing pos.isValid(), the position is explicitly present as a first argument, and the board is implicitly present because of scoping rules. So the call, which depends on data from both parties, is only valid for methods declared in the scope of the Board class.

Here’s a challenge: make a syntax that would clearly express the dependence on two first arguments!