using System.Collections.Generic;
var response = await new HttpClient().GetStringAsync("https://tompafireadventofcode.free.beeceptor.com/day9");
var rows = response.Split("\n").Select((line, rowIndex) =>
var values = line.Select((val, columnIndex) => new Cell(int.Parse(val.ToString()), columnIndex, rowIndex)).ToList();
return new Row(values, rowIndex);
var heightMap = new HeightMap(rows);
var lowestPointsPerRow = heightMap.Rows.SelectMany(row =>
int? prev = val.ColumnIndex > 0 ? row.Values[val.ColumnIndex - 1].Value : null;
int? next = val.ColumnIndex < row.Values.Count - 1 ? row.Values[val.ColumnIndex + 1].Value : null;
return prev.GetValueOrDefault(val.Value + 1) > val.Value && next.GetValueOrDefault(val.Value + 1) > val.Value;
var lowestPointsPerColumns = heightMap.Columns.SelectMany(col =>
int? prev = val.RowIndex > 0 ? col.Values[val.RowIndex - 1].Value : null;
int? next = val.RowIndex < col.Values.Count - 1 ? col.Values[val.RowIndex + 1].Value : null;
return prev.GetValueOrDefault(val.Value + 1) > val.Value && next.GetValueOrDefault(val.Value + 1) > val.Value;
var lowestPoints = lowestPointsPerRow.Intersect(lowestPointsPerColumns).ToList();
var riskLevels = lowestPoints.Select(val => 1 + val.Value);
var result = riskLevels.Sum();
Console.WriteLine(result);
var basins = lowestPoints.Select(val => GetBasin(heightMap, val)).ToList();
var top3BasinCounts = basins.Select(b => b.Count()).OrderByDescending(c => c).Take(3);
var result2 = top3BasinCounts.Aggregate(1, (x, b) => x * b);
Console.WriteLine(result2);
List<Cell> GetBasin(HeightMap heightMap, Cell cell)
var row = GetRow(heightMap, cell);
var col = GetColumn(heightMap, cell);
var basin = new List<Cell>
NavigatePrevious(row.Values, cell, x => x.ColumnIndex),
NavigateNext(row.Values, cell, x => x.ColumnIndex),
NavigatePrevious(col.Values, cell, x => x.RowIndex),
NavigateNext(col.Values, cell, x => x.RowIndex),
var matches = adjacent.Where(x => IsMatch(x, cell.Value)).Select(x => x!).ToList();
.SelectMany(x => GetBasin(heightMap, x)));
basin = basin.Distinct().ToList();
bool IsMatch(Cell? cell, int compareToValue)
return cell != null && cell.Value != 9 && cell.Value > compareToValue;
Cell? NavigatePrevious(IEnumerable<Cell> values, Cell cell, Func<Cell, int> indexFunc)
return indexFunc(cell) > 0 ? values.ElementAt(indexFunc(cell) - 1) : null;
Cell? NavigateNext(IEnumerable<Cell> values, Cell cell, Func<Cell, int> indexFunc)
return indexFunc(cell) < values.Count() - 1 ? values.ElementAt(indexFunc(cell) + 1) : null;
Row GetRow(HeightMap heightMap, Cell cell)
return heightMap.Rows.Single(row => row.Index == cell.RowIndex);
Column GetColumn(HeightMap heightMap, Cell cell)
return heightMap.Columns.Single(col => col.Index == cell.ColumnIndex);
public record Row(List<Cell> Values, int Index);
public record Column(List<Cell> Values, int Index);
public record Cell(int Value, int ColumnIndex, int RowIndex);
public record HeightMap(IEnumerable<Row> Rows)
public List<Column> Columns =>
Rows.SelectMany(row => row.Values)
.GroupBy(x => x.ColumnIndex)
.Select(x => new Column(x.ToList(), x.Key)).ToList();