def dp_max_coin_value(dp, coins, i, j):
  """
  Recursive function which computes the maximum
  possible value assuming the opponent also plays
  optimally

  It uses memoization to speed up performance
  with a Python dictionary named dp "dynamic programming"

  """
  if (i, j) in dp:
    return dp[(i, j)]
  if i > j:
    return 0
  elif i == j:
    dp[(i, j)] = coins[i]
  else:
    dp[(i, j)] = max(
      coins[i] + min(
        dp_max_coin_value(dp, coins, i + 1, j - 1),
        dp_max_coin_value(dp, coins, i + 2, j),
      ),
      coins[j] + min(
        dp_max_coin_value(dp, coins, i + 1, j - 1),
        dp_max_coin_value(dp, coins, i, j - 2),
      ),
    )
  return dp[(i, j)]