Skip to content

Writing a Computer Player

Rainer Stropek edited this page Oct 9, 2020 · 7 revisions

How to Write a Computer Player

Introduction

This wiki pages describes how you can build a computer player.

Writing the Code

Your computer player needs to be in the NBattleshipCodingContest.Players project.

A computer player needs to derive from PlayerBase. Particularly important is its GetShot method that you have to implement. Let's look at a simple example (from RandomShots.cs) that just does random shots:

using NBattleshipCodingContest.Logic;
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

/// <summary>
/// Implements a battleship player that randomly shoots at squares
/// </summary>
public class RandomShots : PlayerBase
{
    /// <inheritdoc />
    public override async Task GetShot(Guid _, string __, IReadOnlyBoard ___, Shoot shoot)
    {
        // Return a random shot between A1 and J10
        var rand = new Random();
        await shoot(new BoardIndex(rand.Next(10), rand.Next(10)));
    }
}

GetShot receives the following parameters:

Parameter Description
gameId Unique identifier of the current game. Use it to identify all the shots that belong to a single game. Simple players will probably ignore this parameter.
opponent Identifier of the opponent. Use this if you want to adjust your strategy based on the opponent. Simple players will probably ignore this parameter.
board Current board content (see also IReadOnlyBoard).
shoot Callback with which the method has to do the shooting. Pass it the location where you want to shoot. The result it the shot's result (water or hit).

Here is a more sophisticated sample with a player that does not shoot twice on the same square (from SmartRandomShots.cs):

using NBattleshipCodingContest.Logic;
using System;
using System.Threading.Tasks;

/// <summary>
/// Implements a battleship player that randomly shoots at squares
/// </summary>
/// <remarks>
/// This player is smarter than <see cref="RandomShots"/> because it does not
/// shoot on squares that have already been shot at.
/// </remarks>
public class SmartRandomShots : PlayerBase
{
    /// <inheritdoc />
    public override async Task GetShot(Guid _, string __, IReadOnlyBoard board, Shoot shoot)
    {
        var rand = new Random();
        while (true)
        {
            var ix = new BoardIndex(rand.Next(10), rand.Next(10));
            if (board[ix] == SquareContent.Unknown)
            {
                await shoot(ix);
                break;
            }
        }
    }
}

Players With State

The system creates a new instance of the player for each shot. Therefore, we need to store state in a static dictionary and access the appropriate state using the game's ID.

Players do not run in parallel. Therefore, it is not necessary to synchronize access to the dictionary with locking.

Here is an example for a player with state:

/// <summary>
/// Implements a battleship player that shoots at one cell after the other
/// </summary>
public class SequentialWithState : PlayerBase
{
    // The system create a new instance of the player for each shot. Therefore,
    // we need to store state in a static dictionary. Players do not run in parallel.
    // Therefore, it is not necessary to synchronize access to the dictionary with locking.
    private static readonly Dictionary<Guid, BoardIndex> indexes = new Dictionary<Guid, BoardIndex>();

    /// <inheritdoc />
    public override async Task GetShot(Guid gameId, string __, IReadOnlyBoard board, Shoot shoot)
    {
        if (!indexes.TryGetValue(gameId, out BoardIndex ix))
        {
            ix = indexes[gameId] = new BoardIndex();
        }

        // Shoot at first current square
        await shoot(ix);
        indexes[gameId]++;
    }
}

Tips

  • Trying to find out the location of the ships by using reflection is useless. Games run in separate processes where the location of the ships is not known. You would have to access process of a separate process (or in some cases even Docker containers). Good luck with that ;-)

Clone this wiki locally