A chess board is normally played with 16 pawns and 16 other pieces, for this kata a variant will be played with only the pawns.  All other pieces will not be on the board.  
For information on how pawns move, refer [here](http://www.chesscorner.com/tutorial/basic/pawn/pawn.htm)
  
  
Write a function that can turn a list of pawn moves into a visual representation of the resulting board.  
A chess move will be represented by a string,
```
"c3"
```
This move represents a pawn moving to `c3`.  If it was white to move, the move would represent a pawn from `c2` moving to `c3`.  If it was black to move, a pawn would move from `c4` to `c3`, because black moves in the other direction.  
The first move in the list and every other move will be for white's pieces.
  
The letter represents the column, while the number represents the row of the square where the piece is moving  
    
Captures are represented differently from normal moves:

```
"bxc3"
```
represents a pawn on the column represented by 'b' (the second column) capturing a pawn on `c3`.

For the sake of this kata a chess board will be represented by a list like this one:  
```
[[".",".",".",".",".",".",".","."],
 ["p","p","p","p","p","p","p","p"],
 [".",".",".",".",".",".",".","."],
 [".",".",".",".",".",".",".","."],
 [".",".",".",".",".",".",".","."],
 [".",".",".",".",".",".",".","."],
 ["P","P","P","P","P","P","P","P"],
 [".",".",".",".",".",".",".","."]]
```
Here is an example of the board with the squares labeled:
```
[["a8","b8","c8","d8","e8","f8","g8","h8"],
 ["a7","b7","c7","d7","e7","f7","g7","h7"],
 ["a6","b6","c6","d6","e6","f6","g6","h6"],
 ["a5","b5","c5","d5","e5","f5","g5","h5"],
 ["a4","b4","c4","d4","e4","f4","g4","h4"],
 ["a3","b3","c3","d3","e3","f3","g3","h3"],
 ["a2","b2","c2","d2","e2","f2","g2","h2"],
 ["a1","b1","c1","d1","e1","f1","g1","h1"]]
```
White pawns are represented by capital `'P'` while black pawns are lowercase `'p'`.  
  
      
A few examples
```
If the list/array of moves is: ["c3"]
>>>
[[".",".",".",".",".",".",".","."],
 ["p","p","p","p","p","p","p","p"],
 [".",".",".",".",".",".",".","."],
 [".",".",".",".",".",".",".","."],
 [".",".",".",".",".",".",".","."],
 [".",".","P",".",".",".",".","."],
 ["P","P",".","P","P","P","P","P"],
 [".",".",".",".",".",".",".","."]]
```
add a few more moves,
```
If the list/array of moves is: ["d4", "d5", "f3", "c6", "f4"]
>>>
[[".",".",".",".",".",".",".","."],
 ["p","p",".",".","p","p","p","p"],
 [".",".","p",".",".",".",".","."],
 [".",".",".","p",".",".",".","."],
 [".",".",".","P",".","P",".","."],
 [".",".",".",".",".",".",".","."],
 ["P","P","P",".","P",".","P","P"],
 [".",".",".",".",".",".",".","."]]
```
now to add a capture...
```
If the list/array of moves is: ["d4", "d5", "f3", "c6", "f4", "c5", "dxc5"]
>>>
[[".",".",".",".",".",".",".","."],
 ["p","p",".",".","p","p","p","p"],
 [".",".",".",".",".",".",".","."],
 [".",".","P","p",".",".",".","."],
 [".",".",".",".",".","P",".","."],
 [".",".",".",".",".",".",".","."],
 ["P","P","P",".","P",".","P","P"],
 [".",".",".",".",".",".",".","."]]
```
If an invalid move (a move is added that no pawn could perform, a capture where there is no piece, a move to a square where there is already a piece, etc.) is found in the list of moves, return '(move) is invalid'.
```python
If the list/array of moves is: ["e6"]
>>>
"e6 is invalid"
```

```python
If the list/array of moves is: ["e4", "d5", "exf5"]
>>>
"exf5 is invalid"
```
The list passed to `pawn_move_tracker / PawnMoveTracker.movePawns` will always be a list of strings in the form (regex pattern): `[a-h][1-8]` or `[a-h]x[a-h][1-8]`.  

Notes:

* In the case of a capture, the first lowercase letter will always be adjacent to the second in the alphabet, a move like `axc5` will never be passed.
* A pawn can move two spaces on its first move
* There are no cases with the 'en-passant' rule.

```python

USER_FILENAME = "<user_code>"
import sys, json, builtins
import math, heapq, bisect, itertools, functools, collections, statistics, fractions, decimal, operator, array, random, re, string, os, pathlib
try:
    import resource as _res
except Exception:
    _res = None
from collections import Counter, defaultdict, deque, OrderedDict
from functools import lru_cache, reduce
from itertools import accumulate, permutations, combinations, product
from math import gcd, ceil, floor, sqrt
from decimal import Decimal
try:
    from typing import *
except Exception:
    pass
try:
    sys.setrecursionlimit(1_000_000)
except Exception:
    pass
try:
    import site as _site
    try:
        _sys_sites = _site.getsitepackages() if hasattr(_site, "getsitepackages") else []
        for _p in _sys_sites:
            if _p and _p not in sys.path:
                sys.path.append(_p)
    except Exception:
        pass
    try:
        _usp = _site.getusersitepackages()
        if isinstance(_usp, str):
            _usp = [_usp]
        for _p in _usp:
            if _p and _p not in sys.path:
                sys.path.append(_p)
    except Exception:
        pass
except Exception:
    pass

def _disabled_input(*args, **kwargs):
    raise RuntimeError("input() is disabled in callable mode")
builtins.input = _disabled_input

ns = {"__name__": "__judge__"}  # prevent if __name__ == "__main__" blocks
for _k, _v in list(globals().items()):
    if not _k.startswith("_"):
        ns[_k] = _v
_CODE = 'def pawn_move_tracker(moves):\n    board_state = [\n        [".",".",".",".",".",".",".","."],\n        ["p","p","p","p","p","p","p","p"],\n        [".",".",".",".",".",".",".","."],\n        [".",".",".",".",".",".",".","."],\n        [".",".",".",".",".",".",".","."],\n        [".",".",".",".",".",".",".","."],\n        ["P","P","P","P","P","P","P","P"],\n        [".",".",".",".",".",".",".","."]\n    ]\n\n    board = Board(board_state)\n    next_board = board\n    player = \'P\'\n\n    for move in moves:\n        next_board = board.play(move, player)\n        if(next_board == None):\n            return move + \' is invalid\'\n\n        player = \'P\' if player == \'p\' else \'p\'\n\n    return board.get_state()\n\nclass Pawn:\n    def __init__(self, cell, player):\n        self.cell = cell\n        self.moved = False\n        self.player = player\n        self.is_white = player == \'P\'\n        self.is_black = player == \'p\'\n        self.forward_direction = -1 if self.is_white else 1;\n\n    def moveTo(self, cell):\n        self.moved = True\n        self.cell.remove_pawn()\n        cell.add_pawn(self)\n        self.cell = cell\n        return cell\n\n    def belongsTo(self, player):\n        return self.player == player\n\n    # returns true if this pawn can move to the given cell in the context of a chess board\n    def canMoveTo(self, cell, board):\n        # can\'t move ahead if cell is occupied\n        if cell.is_occupied():\n            return False\n\n        steps_away = cell.row - self.cell.row\n\n        if steps_away == self.forward_direction * 1:\n            return True\n\n        if steps_away == self.forward_direction * 2:\n            cell_ahead = board.cellAt(self.cell.row + self.forward_direction, self.cell.col)\n            return not self.moved and not cell_ahead.is_occupied()\n\n    def canCapture(self, cell):\n        cell_has_enemy = cell.is_occupied() and cell.pawn.player != self.player\n        cell_is_ahead = cell.row - self.cell.row == self.forward_direction\n        cell_is_adjacent = abs(cell.col - self.cell.col)\n        return cell_has_enemy and cell_is_ahead and cell_is_adjacent\n\nclass Cell:\n    def __init__(self, row, col, occupant):\n        self.row = row\n        self.col = col\n        self.occupant = occupant\n\n        if(occupant != \'.\'):\n           self.pawn = Pawn(self, occupant)\n        else:\n           self.pawn = None\n\n    def is_occupied(self):\n        return not self.pawn == None\n\n    def add_pawn(self, pawn):\n        self.pawn = pawn\n        self.occupant = pawn.player\n\n    def remove_pawn(self):\n        self.pawn = None\n        self.occupant = \'.\'\n\nclass Board:\n    def __init__(self, board_array):\n        self.board_state = []\n\n        for i, row in enumerate(board_array):\n            board_row = []\n            for j, occupant in enumerate(row):\n                board_row.append(Cell(i,j, occupant))\n\n            self.board_state.append(board_row)\n\n    # returns all pawns in a given column. At most 2 pawns per column.\n    def pawnsInColumn(self, col):\n        cells = [row[col] for row in self.board_state]\n        return [cell.pawn for cell in cells if cell.is_occupied()]\n\n    def cellAt(self, row, col):\n        if(row < 0 or row > 7 or col < 0 or col > 7):\n            return None\n        return self.board_state[row][col]\n\n    def play(self, move, player):\n        if(len(move) == 2):\n           return self.playMove(move, player)\n        else:           \n           return self.playCapture(move, player)\n\n    def playMove(self, move, player):\n        row, col = toCoords(move)\n        cell = self.cellAt(row, col)\n        pawns = self.pawnsInColumn(col)\n\n        for pawn in pawns:\n            if pawn.belongsTo(player) and pawn.canMoveTo(cell, self):\n                pawn.moveTo(cell)\n                return self\n        return None\n\n    def playCapture(self, move, player):\n        col = toCol(move[0])\n        capture_row, capture_col = toCoords(move[2:])\n        cell = self.cellAt(capture_row, capture_col)\n        pawns = self.pawnsInColumn(col)\n\n        for pawn in pawns:\n            if pawn.belongsTo(player) and pawn.canCapture(cell):\n                pawn.moveTo(cell)\n                return self\n        return None\n\n    def get_state(self):\n        return list([list([cell.occupant for cell in row]) for row in self.board_state])\n\ndef toCoords(move):\n    col = toCol(move[0])\n    row = 8 - int(move[1])\n    return [row, col]\n\ndef toCol(move):\n    return (ord(move) - 97)\n'
_co = compile(_CODE, USER_FILENAME, "exec")
exec(_co, ns, ns)

_target = None
_fn_name = 'pawn_move_tracker'
if _fn_name:
    if "Solution" in ns:
        try:
            _obj = ns["Solution"]()
            if hasattr(_obj, _fn_name):
                _target = getattr(_obj, _fn_name)
        except Exception:
            _target = None
    if _target is None and _fn_name in ns and callable(ns[_fn_name]):
        _target = ns[_fn_name]
# Fallback: if there's exactly one user-defined function, use it
if _target is None:
    _cands = [v for k, v in ns.items() if callable(v) and getattr(v, "__module__", "") in ("__judge__", "__main__")]
    _user_funcs = [c for c in _cands if getattr(c, "__name__", "").startswith("_") is False]
    if len(_user_funcs) == 1:
        _target = _user_funcs[0]
if _target is None:
    raise RuntimeError(f"Could not resolve callable '{_fn_name}'")

_raw = sys.stdin.read().strip()
if _raw:
    _parsed = json.loads(_raw)
    if isinstance(_parsed, dict):
        _kwargs = _parsed.get("kwargs", {})
        _args = _parsed.get("args", [])
    else:
        _args, _kwargs = _parsed, {}
else:
    _args, _kwargs = [], {}
if not isinstance(_args, (list, tuple)):
    _args = [_args]
_res = _target(*_args, **_kwargs)
try:
    sys.stdout.write(repr(_res) + "\n")
except Exception:
    pass

```

Measured memory_bytes: 23794