Add project files.

This commit is contained in:
Michael Chen 2022-12-20 23:02:29 +01:00
parent 1695d2a18b
commit 9bd9a063f8
Signed by: cnml
GPG Key ID: 5845BF3F82D5F629
3 changed files with 208 additions and 0 deletions

9
Codingame.csproj Normal file
View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

25
Codingame.sln Normal file
View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33122.133
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codingame", "Codingame.csproj", "{4C43862B-37C5-4543-BD26-08875DC59CAC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4C43862B-37C5-4543-BD26-08875DC59CAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4C43862B-37C5-4543-BD26-08875DC59CAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4C43862B-37C5-4543-BD26-08875DC59CAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4C43862B-37C5-4543-BD26-08875DC59CAC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4EE8EC05-1729-4E48-AC79-5847801FFCA1}
EndGlobalSection
EndGlobal

174
FallChallenge2022.cs Normal file
View File

@ -0,0 +1,174 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Codingame {
public class FallChallenge2022 {
public static string ReadInput() => Console.ReadLine() ?? throw new InvalidProgramException("No input available!");
public static void Main(string[] args) {
string[] inputs;
inputs = ReadInput().Split(' ');
var width = int.Parse(inputs[0]);
var height = int.Parse(inputs[1]);
var state = new GameState(width, height, new FieldState[width, height]);
// game loop
while (true) {
inputs = ReadInput().Split(' ');
state.MyMatter = int.Parse(inputs[0]);
state.OppMatter = int.Parse(inputs[1]);
for (var i = 0; i < height; i++) for (var j = 0; j < width; j++) {
ref var field = ref state.Fields[j, i];
inputs = ReadInput().Split(' ');
field.ScrapAmount = int.Parse(inputs[0]);
field.Owner = (Owner)int.Parse(inputs[1]);
if (!Enum.IsDefined(field.Owner)) throw new InvalidProgramException("Invalid owner!");
field.Units = int.Parse(inputs[2]);
field.Recycler = int.Parse(inputs[3]) == 1;
field.CanBuild = int.Parse(inputs[4]) == 1;
field.CanSpawn = int.Parse(inputs[5]) == 1;
field.InRangeOfRecycler = int.Parse(inputs[6]) == 1;
}
// Write an action using Console.WriteLine()
// To debug: Console.Error.WriteLine("Debug messages...");
var commands = GetCommands(state).ToList() is { Count: > 0 } cmds ? cmds : Command.Empty;
Console.WriteLine(string.Join(';', commands.Select(i => i.GetCommandString())));
}
}
public static IEnumerable<Command> GetCommands(GameState state) {
var units = state.FindUnits();
var neutral = state.FindFields(Owner.Neutral);
var recyclers = state.FindFields(i => i.Recycler).Keys.ToList();
var possibleBuilds = state.PossibleRecyclerSpots(recyclers).OrderByDescending(i => i.Value).Take(1).ToList();
while (possibleBuilds.Count > 0) {
var possibleBuild = possibleBuilds.First();
if (state.MyMatter < GameState.RecyclerCost) break;
state.MyMatter -= GameState.RecyclerCost;
Console.Error.WriteLine($"Building recycler at {possibleBuild.Key} for {possibleBuild.Value} amount");
yield return new Build(possibleBuild.Key);
recyclers.Add(possibleBuild.Key);
possibleBuilds = state.PossibleRecyclerSpots(recyclers).OrderByDescending(i => i.Value).Take(1).ToList();
}
foreach (var (position, amount) in units) {
for (int i = 0; i < amount; i++) {
if (neutral.Count == 0) yield break;
var emptySpot = neutral.Keys.First();
if (!neutral.Remove(emptySpot, out var field)) throw new InvalidProgramException();
yield return new Move(1, position, emptySpot);
}
}
}
}
public abstract record class Command {
public virtual string GetCommandString() => $"{Name} {string.Join(' ', Args.Select(i => i.ToString()))}";
protected virtual IEnumerable<object> Args { get; } = Enumerable.Empty<object>();
public virtual string Name => GetType().Name.ToUpper();
public static List<Command> Empty { get; } = new() { new Wait() };
}
public abstract record class CoordinateCommand(int X, int Y) : Command {
public CoordinateCommand(Coordinate2D Coord) : this(Coord.X, Coord.Y) { }
protected override IEnumerable<object> Args {
get {
yield return X;
yield return Y;
}
}
}
public record class Move(int Amount, int FromX, int FromY, int ToX, int ToY) : Command {
public Move(int Amount, Coordinate2D From, Coordinate2D To) : this(Amount, From.X, From.Y, To.X, To.Y) { }
protected override IEnumerable<object> Args {
get {
yield return Amount;
yield return FromX;
yield return FromY;
yield return ToX;
yield return ToY;
}
}
}
public record class Build(int X, int Y) : CoordinateCommand(X, Y) {
public Build(Coordinate2D coord) : this(coord.X, coord.Y) { }
}
public record class Spawn(int Amount, int X, int Y) : CoordinateCommand(X, Y) {
public Spawn(int Amount, Coordinate2D Coord) : this(Amount, Coord.X, Coord.Y) { }
protected override IEnumerable<object> Args => base.Args.Prepend(Amount);
}
public record class Wait : Command;
public record class Message : Command;
public enum Owner {
Player = 1,
Enemy = 0,
Neutral = -1
}
public record struct GameState(int Width, int Height, FieldState[,] Fields, int MyMatter = 0, int OppMatter = 0) {
public const int RecyclerCost = 10;
public FieldState FieldAt(Coordinate2D coord) => this[coord];
public ref FieldState this[Coordinate2D coord] => ref Fields[coord.X, coord.Y];
public Dictionary<Coordinate2D, int> PossibleRecyclerSpots(IReadOnlyCollection<Coordinate2D> recyclers) {
Dictionary<Coordinate2D, int> found = new();
for (int y = 0; y < Height; y++)
for (int x = 0; x < Width; x++) {
Coordinate2D coord = new(x, y);
var field = this[coord];
if (!field.CanBuild) continue;
var potentialMaterial = coord.Neighbors().Append(coord)
.Where(WithinBounds)
.Where(i => !NearRecycler(recyclers, i))
.Select(FieldAt)
.Sum(i => i.ScrapAmount);
if (potentialMaterial != 0)
found[coord] = potentialMaterial;
}
return found;
}
public static bool NearRecycler(IReadOnlyCollection<Coordinate2D> recyclers, Coordinate2D coord)
=> recyclers.Any(i => (i - coord).ManhattanDistance < 2);
public Dictionary<Coordinate2D, int> FindWhere(Func<FieldState, int> find) {
Dictionary<Coordinate2D, int> found = new();
for (int y = 0; y < Height; y++)
for (int x = 0; x < Width; x++) {
Coordinate2D coord = new(x, y);
var amount = find(this[coord]);
if (amount != 0) found[coord] = found.GetValueOrDefault(coord, 0) + amount;
}
return found;
}
public bool WithinBounds(Coordinate2D coord) => coord.X >= 0 && coord.X < Width && coord.Y >= 0 && coord.Y < Height;
public Dictionary<Coordinate2D, FieldState> FindFields(Func<FieldState, bool> prediate) {
Dictionary<Coordinate2D, FieldState> found = new();
for (int y = 0; y < Height; y++)
for (int x = 0; x < Width; x++) {
Coordinate2D coord = new(x, y);
var field = this[coord];
if (prediate(field)) found[coord] = field;
}
return found;
}
public Dictionary<Coordinate2D, FieldState> FindFields(Owner owner)
=> FindFields(i => i.Owner == owner);
public Dictionary<Coordinate2D, int> FindUnits(Owner owner = Owner.Player)
=> FindWhere(i => i.Owner == owner && i is { Units: > 0 and var amount } ? amount : 0);
}
public record struct Coordinate2D(int X, int Y) {
public override string ToString() => $"{X},{Y}";
public IEnumerable<Coordinate2D> Neighbors() {
yield return this with { X = X + 1 };
yield return this with { X = X - 1 };
yield return this with { Y = Y + 1 };
yield return this with { Y = Y - 1 };
}
public static Coordinate2D operator -(Coordinate2D left, Coordinate2D right) => new(left.X - right.X, left.Y - right.Y);
public static Coordinate2D operator +(Coordinate2D left, Coordinate2D right) => new(left.X + right.X, left.Y + right.Y);
public int ManhattanDistance => Math.Abs(X) + Math.Abs(Y);
}
public record struct FieldState(int ScrapAmount, Owner Owner, int Units, bool Recycler, bool CanBuild, bool CanSpawn, bool InRangeOfRecycler);
}