using Accord.Video.FFMPEG; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Text.RegularExpressions; namespace D17.Bonus { class Program { enum EnumType { Clay, StillWater, RunningWater } static int panY = 0; static void Main(string[] args) { if (args.Length < 1) return; if (File.Exists(args[0]) == false) return; var file = File.OpenText(args[0]); var reg = new Regex(@"[xy]=(\d+), [xy]=(\d+)[.][.](\d+)"); var map = new Dictionary<(int x, int y), EnumType>(); var rwater = new Accord.Collections.OrderedDictionary<(int x, int y), (int x, int y)>(); int MAXY = 0; int MINX = int.MaxValue; int MINY = int.MaxValue; int MAXX = 0; do { var line = file.ReadLine(); if (line == null) break; var result = reg.Match(line); CreateMap(map, ref MAXY, ref MINX, ref MINY, ref MAXX, line, result); } while (true); file.Close(); // Start with river rwater.Add((500, 0), (500, 0)); map.Add((500, 0), EnumType.RunningWater); Console.SetOut(TextWriter.Null); VideoFileWriter writer = new VideoFileWriter(); var width = MAXX - MINX + 2; if (width % 2 == 1) width++; var height = (width * 3) / 4; if (height % 2 == 1) height++; int tpy = (height * 2) / 3; writer.Open("out.avi", width, height); int rr = 0; while (rwater.Count > 0) { rr++; if (rwater.Any(r => r.Key.y > panY + tpy)) panY++; var enumer = rwater.FirstOrDefault(); var water = enumer.Key; var source = enumer.Value; if (map.ContainsKey((water.x, water.y + 1)) == false && water.y > MAXY) { rwater.Remove(water); continue; } bool renderMap = false; if (map.ContainsKey((water.x, water.y + 1)) == false || map[(water.x, water.y + 1)] == EnumType.RunningWater) { RaindropsOnRoses(map, rwater, water); renderMap = true; } else { ExpandToBothSides(map, rwater, water, source); renderMap = true; int maxX, minX; bool canConvert = CanConvertToStillWater(map, rwater, water, source, out maxX, out minX); if (canConvert) ConvertToStillWater(map, rwater, water, source, maxX, minX); else rwater.Remove(water); } if (renderMap) { if (rr < rwater.Count) continue; RenderMap(map, writer, MINX, MAXY, width, height); rr = 1; } } var lastBitmap = RenderMap(map, writer, MINX, MAXY, width, height); var countEx1 = map.Count(m => (m.Key.y >= MINY && m.Key.y <= MAXY) && (m.Value == EnumType.RunningWater || m.Value == EnumType.StillWater)); var countEx2 = map.Count(m => (m.Key.y >= MINY && m.Key.y <= MAXY) && (m.Value == EnumType.StillWater)); var answer = $"The answer is {countEx1}m3 flooded,\n and {countEx2}m3 still water ☺"; using (var gd = Graphics.FromImage(lastBitmap)) using (var font = new Font(FontFamily.GenericSerif, 12, FontStyle.Regular)) { gd.DrawString(answer, font, Brushes.IndianRed, 10, tpy + 10); writer.WriteVideoFrame(lastBitmap); } } private static void RaindropsOnRoses(Dictionary<(int x, int y), EnumType> map, IDictionary<(int x, int y), (int x, int y)> rwater, (int x, int y) water) { rwater.Remove(water); var drop = (water.x, water.y + 1); if (!rwater.ContainsKey(drop)) rwater.Add(drop, water); map[drop] = EnumType.RunningWater; } private static void ConvertToStillWater(Dictionary<(int x, int y), EnumType> map, IDictionary<(int x, int y), (int x, int y)> rwater, (int x, int y) water, (int x, int y) source, int maxX, int minX) { for (var x = minX; x <= maxX; ++x) { var res = map.TryGetValue((x, water.y), out var type); if (type == EnumType.RunningWater) { map[(x, water.y)] = EnumType.StillWater; rwater.Remove((x, water.y)); } } rwater.Remove(water); if (!rwater.ContainsKey(source)) rwater.Add(source, (source.x, source.y - 1)); map[source] = EnumType.RunningWater; } private static bool CanConvertToStillWater(Dictionary<(int x, int y), EnumType> map, IDictionary<(int x, int y), (int x, int y)> rwater, (int x, int y) water, (int x, int y) source, out int maxX, out int minX) { bool canConvert = true; maxX = 0; minX = 0; for (int x = water.x; canConvert; ++x) // left to right { maxX = x; var res = map.TryGetValue((x, water.y), out var type); if (res == false) canConvert = false; else if (type == EnumType.Clay) break; } for (int x = water.x; canConvert; --x) // right to left { minX = x; var res = map.TryGetValue((x, water.y), out var type); if (res == false) canConvert = false; else if (type == EnumType.Clay) break; } return canConvert; } private static void ExpandToBothSides(Dictionary<(int x, int y), EnumType> map, IDictionary<(int x, int y), (int x, int y)> rwater, (int x, int y) water, (int x, int y) source) { var next = water; while (true) { var last = next; next = (last.x - 1, water.y); var res = map.TryGetValue((next.x, water.y), out var value); if (res && value == EnumType.Clay) break; rwater.Remove(last); if (!rwater.ContainsKey(next)) rwater.Add(next, source); map[next] = EnumType.RunningWater; var hasBottom = map.TryGetValue((next.x, next.y + 1), out var bottom); if (hasBottom == false || bottom == EnumType.RunningWater) break; } next = water; while (true) { var last = next; next = (last.x + 1, water.y); var res = map.TryGetValue((next.x, water.y), out var value); if (res && value == EnumType.Clay) break; rwater.Remove(last); if (!rwater.ContainsKey(next)) rwater.Add(next, source); map[next] = EnumType.RunningWater; var hasBottom = map.TryGetValue((next.x, next.y + 1), out var bottom); if (hasBottom == false || bottom == EnumType.RunningWater) break; } } private static void CreateMap(IDictionary<(int x, int y), EnumType> map, ref int MAXY, ref int MINX, ref int MINY, ref int MAXX, string line, Match result) { var c1 = int.Parse(result.Groups[1].Value); var rmin = int.Parse(result.Groups[2].Value); var rmax = int.Parse(result.Groups[3].Value); if (line.StartsWith("x")) { if (c1 > MAXX) MAXX = c1; if (c1 < MINX) MINX = c1; } else { if (c1 > MAXY) MAXY = c1; if (c1 < MINY) MINY = c1; } for (int i = rmin; i <= rmax; ++i) { if (line.StartsWith("x")) { if (i > MAXY) MAXY = i; if (i < MINY) MINY = i; map[(c1, i)] = EnumType.Clay; } else { if (i > MAXX) MAXX = i; if (i < MINX) MINX = i; map[(i, c1)] = EnumType.Clay; } } } static Bitmap _REFBMP = null; private static Bitmap RenderMap(Dictionary<(int x, int y), EnumType> map, VideoFileWriter writer, int MINX, int MAXY, int width, int height) { if (_REFBMP == null) { _REFBMP = new Bitmap(width, height); for (int x = 0; x < width; ++x) for (int y = 0; y < height; ++y) _REFBMP.SetPixel(x, y, Color.FloralWhite); } var bmp = new Bitmap(_REFBMP); var hh = Math.Min(height + panY, MAXY); var sortedMap = map.OrderBy(m => m.Key.y).Where(m => m.Key.y > panY && m.Key.y <= hh); foreach (var res in sortedMap) { var type = res.Value; var px = res.Key.x - MINX + 1; var py = res.Key.y - panY - 1; if (type == EnumType.Clay) bmp.SetPixel(px, py, Color.Maroon); else if (type == EnumType.RunningWater) bmp.SetPixel(px, py, Color.Aqua); else if (type == EnumType.StillWater) bmp.SetPixel(px, py, Color.Aquamarine); } writer.WriteVideoFrame(bmp); return bmp; } } }