A board game has many pieces but only a few kinds of piece. The kind decides the name, the symbol and how the piece moves; the piece itself has an owner and a square. The move rule is shared behavior that runs against each piece's own position.
Piece and Board are provided in the starter code. Implement only the flyweight side: the PieceType contract, ConcretePieceType, and PieceTypeFactory (IPieceType in C#).
PieceType exposes describe(owner, x, y), which returns "[runner R] white at (3,0)", and canReach(fromX, fromY, toX, toY).ConcretePieceType(name, symbol, dx, dy, range) moves along the vector (dx, dy). canReach returns true when the target is (fromX + k*dx, fromY + k*dy) or (fromX - k*dx, fromY - k*dy) for some k from 1 to range.PieceTypeFactory exposes get(name, symbol, dx, dy, range), which returns the one shared type for those values and creates it on first request, idOf(type), which numbers types from 1 in creation order, and count().The provided Piece(type, owner, x, y) checks the eight by eight board bounds and asks the type whether the geometry allows a move. The provided Board exposes this API:
Board() creates an empty board.int place(String name, String symbol, int dx, int dy, int range, String owner, int x, int y) puts a new piece on the board and returns its index, counting from 0. A square off the board or already occupied returns -1 and creates nothing.int typeId(String name, String symbol, int dx, int dy, int range) returns the number of the shared type, creating it when absent.int typeCount() and int pieceCount() report the two collections.boolean canMove(int index, int x, int y) reports whether that piece may move to that square: the index is valid, the square is on the board, the type's rule allows it, and the square is empty.boolean move(int index, int x, int y) performs the move when canMove allows it.String describe(int index) describes that piece, or returns "MISSING".The tests call the provided Board; your work should be confined to the contract, the concrete type, and the factory.
Input:
Output:
Explanation: A runner steps along the x axis only. After moving to (3,0) it can move back toward (2,0), because the reverse direction is allowed too.
Input:
Output:
Explanation: A jumper with range one reaches exactly one square in each direction. Off-board squares are refused, an occupied square is refused, and both jumpers share one type.
1 <= name.length <= 10, symbol is one character and 1 <= owner.length <= 10-2 <= dx, dy <= 2, not both zero, and 1 <= range <= 7-2 <= x, y <= 9 and -1 <= index <= 10100 calls in total are made across all methods.Implement PieceType, ConcretePieceType and PieceTypeFactory. Piece and Board are complete and must not be modified.
Full marks when `ConcretePieceType` stores name, symbol, step and range, `canReach` takes both positions as arguments, and the piece keeps its own position and owner. Lose points heavily when the type stores a position or the piece re-implements the move rule.
Full marks when `canReach` accepts exactly the squares at `from + k*(dx,dy)` and `from - k*(dx,dy)` for `k` from `1` to `range`, and nothing else. Lose points when zero steps, steps beyond the range, or a swapped step vector are accepted.
Full marks when equal name, symbol, step and range return the identical object, so many pieces of one kind hold one type, `idOf` follows creation order and `count` matches. Lose points for printing to stdout.
Passing every test is not enough on its own. A submission is accepted only when the design also clears the bar.
| Call | Returns |
|---|---|
| new Board() | null |
| place("runner", "R", 1, 0, 7, "white", 0, 0) | 0 |
| canMove(0, 5, 0) | true |
| canMove(0, 0, 5) | false |
| move(0, 3, 0) | true |
| describe(0) | "[runner R] white at (3,0)" |
| canMove(0, 2, 0) | true |
A runner steps along the x axis only. After moving to (3,0) it can move back toward (2,0), because the reverse direction is allowed too.
Run checks these cases. Submit also runs a larger hidden set.

