Skip to content

Instantly share code, notes, and snippets.

@amirrajan
Last active August 21, 2023 07:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amirrajan/941c17e3725e8cf6701a76f4030b9ce5 to your computer and use it in GitHub Desktop.
Save amirrajan/941c17e3725e8cf6701a76f4030b9ce5 to your computer and use it in GitHub Desktop.

Create an algorithm that returns the number of "rooms" given an ascii string like the following:

1100011
1100011
0001000
1000000

1's denote an occupied tile, 0 means unoccupied.

A room is a collection of connected 1's. Tiles do not connect on diagonals.

Examples:

One Room:

100
000
000

One Room:

110
000
000

Two Rooms:

1010
0010
0000

Three Rooms:

100
010
001

Six Rooms:

1100011
1100011
0001000
1010011
// fswatch Program.cs | xargs -n1 -I{} dotnet run
using System;
using System.Linq;
using System.Collections.Generic;
using static System.Sugar;
namespace Kata
{
class Location
{
public Location(int row, int column)
{
Row = row;
Column = column;
}
public int Row { get; set; }
public int Column { get; set; }
public bool IsSame(Location other)
{
return other.Row == Row && other.Column == Column;
}
}
class BuildingAndNeighbors
{
public BuildingAndNeighbors(Building building, IEnumerable<Building> adjacentBuildings)
{
Building = building;
NeighborBuildings = adjacentBuildings.ToList();
}
public Building Building { get; set; }
public List<Building> NeighborBuildings { get; set; }
public bool IsNeighbor(Building candidate)
{
return NeighborBuildings.Any(candidate.IsSame);
}
public bool ContainsNeighbor(IEnumerable<Building> buildings)
{
return buildings.Any(IsNeighbor);
}
public IEnumerable<Building> AndNeighbors()
{
return (new List<Building> { Building }).Concat(NeighborBuildings);
}
}
class Cluster
{
public List<BuildingAndNeighbors> Buildings { get; set; } = new List<BuildingAndNeighbors>();
public override string ToString()
{
return $"Cluster(size: {Buildings.Count()})";
}
}
class Building
{
public Location Location { get; set; }
public int Value { get; set; }
public Building(int row, int column, int value)
{
Location = new Location(row, column);
Value = value;
}
public override string ToString()
{
return $"Building(row: {Location.Row}, col: {Location.Column}, {Value})";
}
public IEnumerable<Location> NeighborLocations()
{
return new[]
{
new Location(Location.Row - 1, Location.Column + 0), // North
new Location(Location.Row + 0, Location.Column + 1), // East
new Location(Location.Row + 1, Location.Column + 0), // South
new Location(Location.Row + 0, Location.Column - 1), // West
};
}
public bool IsNeighborTo(Building candidate)
{
return NeighborLocations().Any(c => c.IsSame(candidate.Location));
}
public bool IsSame(Building other)
{
return Location.IsSame(other.Location);
}
}
class Program
{
static void Main(string[] args)
{
ClusterCount(@"11000");
ClusterCount(@"10100
11100");
ClusterCount(@"11100
10100");
ClusterCount(@"11000
00100
00000
11011
11111");
$"Done. {DateTime.Now}".Print();
}
static int ClusterCount(string asciiMap)
{
var allBuildings = ParseBuildings(asciiMap);
var buildingsWithNeighbors = DetermineNeighbors(allBuildings);
var clusters = MergeClusters(buildingsWithNeighbors);
clusters.Prints();
return clusters.Count();
}
static IEnumerable<Building> ParseBuildings(string map)
{
return map.EachLine().FlatMap(ParseBuildingLine);
}
static IEnumerable<Building> ParseBuildingLine(Line line)
{
return line.Value
.Trim()
.Print()
.EachChar((column, value) =>
new Building(row: line.Index,
column: column,
value: value.ToInt()))
.Where(b => b.Value == 1);
}
static IEnumerable<BuildingAndNeighbors> DetermineNeighbors(IEnumerable<Building> allBuildings)
{
return allBuildings.Map(b =>
new BuildingAndNeighbors(building: b,
adjacentBuildings: NeighborOnMap(allBuildings, b)));
}
static List<Cluster> MergeClusters(IEnumerable<BuildingAndNeighbors> buildingsWithNeighbors)
{
return buildingsWithNeighbors.Fold(new List<Cluster>(), MergeClusters);
}
static IEnumerable<Building> NeighborOnMap(IEnumerable<Building> allBuildings, Building target)
{
return allBuildings.Where(target.IsNeighborTo);
}
static List<Cluster> MergeClusters(BuildingAndNeighbors candidateBuilding, List<Cluster> clusters)
{
var clustersContainingCandidate =
from c in clusters
from b in c.Buildings
where b.ContainsNeighbor(candidateBuilding.AndNeighbors())
select c;
clusters.Remove(clustersContainingCandidate);
var mergedBuildings =
clustersContainingCandidate.FlatMap(r => r.Buildings)
.Distinct()
.Concat(candidateBuilding);
clusters.Add(new Cluster { Buildings = mergedBuildings });
return clusters;
}
}
}
// these are a suite of helper functions that are missing/desperately needed in C#
namespace System
{
public class Line
{
public int Index { get; set; }
public string Value { get; set; }
}
public class Ref<T1, T2>
{
public Ref() { }
public Ref(T1 value, T2 source)
{
Value = value;
Source = source;
}
public T1 Value { get; set; }
public T2 Source { get; set; }
}
public static class Sugar
{
public static T[] Ary<T>(params T[] ts) => ts;
public static T If<T>(bool predicate, T then, T els)
{
if (predicate) return then;
else return els;
}
public static IEnumerable<int> Map(this int n)
{
for (int i = 0; i < n; i++) yield return i;
}
public static IEnumerable<T> Map<T>(this int n, Func<int, T> func) =>
n.Map().Map(func);
public static void Times(this int n, Action<int> action)
{
for (int i = 0; i < n; i++) action(n);
}
public static IEnumerable<T2> MapWithIndex<T1, T2>(this IEnumerable<T1> enumerable, Func<int, T1, T2> func)
{
var asList = enumerable.ToList();
for (int i = 0; i < asList.Count; i++) yield return func(i, asList[i]);
}
public static IEnumerable<T2> Map<T1, T2>(this IEnumerable<T1> enumerable, Func<T1, T2> func)
{
var asList = enumerable.ToList();
for (int i = 0; i < asList.Count; i++) yield return func(asList[i]);
}
public static T2 Fold<T1, T2>(this IEnumerable<T1> enumerable, T2 initialValue, Func<T2, T2> func)
{
var result = initialValue;
foreach (var e in enumerable) result = func(result);
return result;
}
public static T2 Fold<T1, T2>(this IEnumerable<T1> enumerable, T2 initialValue, Func<T1, T2, T2> func)
{
var result = initialValue;
foreach (var e in enumerable) result = func(e, result);
return result;
}
public static T SafeIndex<T>(this T[] array, int index)
{
if (index < 0) return default(T);
if (index >= array.Length) return default(T);
return array[index];
}
public static string Join<T>(this IEnumerable<T> enumerable, string seperator)
{
return string.Join(seperator, enumerable);
}
public static IEnumerable<T> Prints<T>(this IEnumerable<T> t)
{
global::System.Console.WriteLine(t.Count().ToString() + ": " + t.Join(", "));
return t;
}
public static T Print<T>(this T t)
{
global::System.Console.WriteLine(t);
return t;
}
public static IEnumerable<Line> EachLine(this string s)
{
return s.Split('\n').MapWithIndex((i, line) => new Line { Index = i, Value = line });
}
public static IEnumerable<T> EachLine<T>(this string s, Func<int, string, T> func)
{
return s.Split('\n').MapWithIndex(func);
}
public static IEnumerable<T> EachChar<T>(this string s, Func <int, string, T> func)
{
return s.MapWithIndex((i, c) => func(i, c.ToString()));
}
public static IEnumerable<T2> FlatMap<T1, T2>(this IEnumerable<T1> enumerable, Func<T1, IEnumerable<T2>> func)
{
var flattened = new List<T2>();
foreach(var e in enumerable) flattened.AddRange(func(e));
return flattened;
}
public static IEnumerable<Ref<T2, T1>> FlatMapRef<T1, T2>(this IEnumerable<T1> enumerable, Func<T1, IEnumerable<T2>> func)
{
var flattened = new List<Ref<T2, T1>>();
foreach(var e in enumerable) flattened.AddRange(func(e).Map(t2 => new Ref<T2, T1>(value: t2, source: e)));
return flattened;
}
public static int ToInt(this string n)
{
int result = 0;
int.TryParse(n, out result);
return result;
}
public static List<T> Remove<T>(this List<T> list, IEnumerable<T> candidates)
{
list.RemoveAll(l => candidates.Any(c => l.Equals(c)));
return list;
}
public static List<T> Concat<T>(this IEnumerable<T> enumerable, T entry)
{
var list = enumerable.ToList();
list.Add(entry);
return list;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment