| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- 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;
- }
- }
- }
|