Created
November 3, 2014 10:10
-
-
Save math314/24a458d0ffc006d151ff to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Windows.Forms; | |
| using System.Text; | |
| using System.IO; | |
| using System.Drawing; | |
| using System.Reflection; | |
| using System.Text.RegularExpressions; | |
| using System.Linq; | |
| using System.Net.Sockets; | |
| using System.Drawing.Imaging; | |
| namespace csharp { | |
| public static class Ext { | |
| public static void Foreach<T>(this IEnumerable<T> src, Action<T> func) | |
| { | |
| foreach (var s in src) { | |
| func(s); | |
| } | |
| } | |
| } | |
| public class Sok : IDisposable { | |
| TcpClient client; | |
| NetworkStream ns; | |
| StreamReader sr; | |
| StreamWriter sw; | |
| public Sok(string address, int host) | |
| { | |
| client = new TcpClient(address, host); | |
| ns = client.GetStream(); | |
| sr = new StreamReader(ns); | |
| sw = new StreamWriter(ns); | |
| } | |
| public void ReadAll(){ | |
| System.IO.MemoryStream ms = new System.IO.MemoryStream(); | |
| byte[] resBytes = new byte[256]; | |
| do | |
| { | |
| //データの一部を受信する | |
| int resSize = ns.Read(resBytes, 0, resBytes.Length); | |
| //Readが0を返した時はサーバーが切断したと判断 | |
| if (resSize == 0) | |
| { | |
| Console.WriteLine("サーバーが切断しました。"); | |
| break; | |
| } | |
| //受信したデータを蓄積する | |
| ms.Write(resBytes, 0, resSize); | |
| } while (ns.DataAvailable); | |
| //受信したデータを文字列に変換 | |
| string resMsg = Encoding.ASCII.GetString(ms.ToArray()); | |
| ms.Close(); | |
| Console.WriteLine(resMsg); | |
| } | |
| public string ReadLine() | |
| { | |
| while (true) { | |
| string ret = sr.ReadLine(); | |
| if (ret != null) { | |
| if (ret.Length > 60) Console.WriteLine("<read> : " + ret.Substring(0, 60) + "..."); | |
| else Console.WriteLine("<read> : " + ret); | |
| if (ret.Contains("checkp") || ret.Contains("Congrats!")) { | |
| File.AppendAllText("key.txt",ret + "\n"); | |
| } | |
| return ret; | |
| } | |
| Wait(); | |
| } | |
| } | |
| public void WriteLine(string text) | |
| { | |
| Console.WriteLine("<write> : " + text); | |
| sw.Write(text + "\n\n"); // add emptyline | |
| sw.Flush(); | |
| } | |
| public void Wait() | |
| { | |
| System.Threading.Thread.Sleep(10); | |
| } | |
| public void Dispose() | |
| { | |
| ns.Close(); | |
| client.Close(); | |
| } | |
| } | |
| class csharp{ | |
| Image FromBase64(string base64) | |
| { | |
| var png_data = Convert.FromBase64String(base64); | |
| var stream = new MemoryStream(png_data); | |
| Image img = Image.FromStream(stream); | |
| return img; | |
| } | |
| List<bool[]> convertToArray(Image img,int th = 255) | |
| { | |
| List<bool[]> ret = new List<bool[]>(); | |
| for (int i = 0; i < img.Height; i++) ret.Add(new bool[img.Width]); | |
| Bitmap bmp = new Bitmap(img); | |
| BitmapData bmpData = bmp.LockBits( | |
| new Rectangle(Point.Empty, bmp.Size), | |
| ImageLockMode.ReadWrite, | |
| PixelFormat.Format32bppArgb); | |
| unsafe { | |
| byte* pixel = (byte*)bmpData.Scan0; | |
| int dataLength = bmpData.Stride * bmpData.Height; | |
| for (int i = 0; i < dataLength; i += 4) { | |
| byte b = *(pixel++); | |
| byte g = *(pixel++); | |
| byte r = *(pixel++); | |
| byte a = *(pixel++); | |
| ret[i / bmpData.Stride][i % bmpData.Stride / 4] = r < th; // 黒色だけ抽出 | |
| } | |
| } | |
| bmp.UnlockBits(bmpData); | |
| return ret; | |
| } | |
| void SwapList(List<int> l, int i, int j) | |
| { | |
| int tmp = l[i]; | |
| l[i] = l[j]; | |
| l[j] = tmp; | |
| } | |
| // [150,450) 縦棒 | |
| int Amida(List<bool[]> img){ | |
| //縦棒の位置をスキャン | |
| int W = img[0].Length; | |
| List<int> tate = new List<int>(); | |
| for (int i = 0; i < W - 1; i++) { | |
| int H = 151; | |
| if (/*!img[H-1][i] && */ !img[H][i] && img[H][i + 1]) tate.Add(i); | |
| } | |
| List<int> perm = Enumerable.Range(1,tate.Count).ToList(); | |
| for (int i = 150; i < 450; i++) { | |
| for (int j = 0; j < tate.Count - 1; j++) { | |
| int middle = (tate[j] + tate[j + 1]) / 2; | |
| if (!img[i - 1][middle] && img[i][middle]) { | |
| //横棒発見 | |
| SwapList(perm, j, j + 1); | |
| } | |
| } | |
| } | |
| //丸どこ? | |
| int circleH = 470; | |
| for (int i = 0; i < tate.Count; i++) { | |
| if (img[circleH][tate[i]]) { | |
| return perm[i]; | |
| } | |
| } | |
| return -1; //?? | |
| } | |
| int bfs(int sh, int sw, List<bool[]> ary) | |
| { | |
| int ret = 1; | |
| Queue<Point> q = new Queue<Point>(); | |
| q.Enqueue(new Point(sw, sh)); | |
| ary[sh][sw] = false; | |
| while (q.Count != 0) { | |
| Point p = q.Dequeue(); | |
| int h = p.Y; int w = p.X; | |
| int[] dh = { 1, 0, -1, 0 }; | |
| int[] dw = { 0, 1, 0, -1 }; | |
| for (int i = 0; i < 4; i++) { | |
| int nh = h + dh[i], nw = w + dw[i]; | |
| if (0 <= nh && nh < 600 && 0 <= nw && nw < 600) { | |
| if (ary[nh][nw]) { | |
| ary[nh][nw] = false; | |
| ret++; | |
| q.Enqueue(new Point(nw, nh)); | |
| } | |
| } | |
| } | |
| } | |
| return ret; | |
| } | |
| List<bool[]> copy(List<bool[]> ary) | |
| { | |
| List<bool[]> copyed = new List<bool[]>(); | |
| foreach (var x in ary) { | |
| bool[] y = new bool[x.Length]; | |
| for (int i = 0; i < x.Length; i++) y[i] = x[i]; | |
| copyed.Add(y); | |
| } | |
| return copyed; | |
| } | |
| double getNumberDist(Point pt, List<Tuple<Point, int>> l) | |
| { | |
| double mn = 100000; | |
| foreach (var t in l) { | |
| if (t.Item2 > 300) continue;// 黒丸 or アミダ | |
| double dx = pt.X - t.Item1.X; | |
| double dy = pt.Y - t.Item1.Y; | |
| mn = Math.Min(mn,Math.Sqrt(dx * dx + dy * dy)); | |
| } | |
| return mn; | |
| } | |
| List<Tuple<Point, int>> getTuple(List<bool[]> ary) | |
| { | |
| List<bool[]> copyed = copy(ary); | |
| List<Tuple<Point, int>> l = new List<Tuple<Point, int>>(); | |
| for (int h = 0; h < 600; h++) { | |
| for (int w = 0; w < 600; w++) { | |
| if (copyed[h][w]) { | |
| int count = bfs(h, w, copyed); | |
| l.Add(Tuple.Create(new Point(w, h), count)); | |
| } | |
| } | |
| } | |
| return l; | |
| } | |
| List<float> getAngleByConnectedComponents(Image img, List<bool[]> ary) | |
| { | |
| var l = getTuple(ary); | |
| //アミダ以外埋める | |
| Tuple<Point, int> ch = l.Find((t) => t.Item2 == l.Max((y) => y.Item2)); | |
| List<bool[]> copyed = copy(ary); | |
| foreach (var t in l) { | |
| if (ch != t) { | |
| bfs(t.Item1.Y, t.Item1.X, copyed); | |
| } | |
| } | |
| //左上は1に近い.1は最もピクセル数が少ない…?(実は7の方が少ない) | |
| // 右上の数字は? | |
| Tuple<Point, int> one = null, LargestNum = null; | |
| { | |
| double dist = 0; | |
| foreach (var t in l) { | |
| if (t.Item2 > 300) continue;// 黒丸 or アミダ | |
| foreach (var u in l) { | |
| if (u.Item2 > 300) continue;// 黒丸 or アミダ | |
| double dx = t.Item1.X - u.Item1.X; | |
| double dy = t.Item1.Y - u.Item1.Y; | |
| double tmp = Math.Sqrt(dx * dx + dy * dy); | |
| if (tmp > dist) { | |
| dist = tmp; | |
| one = t; | |
| LargestNum = u; | |
| } | |
| } | |
| } | |
| if (one.Item2 > LargestNum.Item2) { | |
| Tuple<Point, int> tmp = one; | |
| one = LargestNum; | |
| LargestNum = tmp; | |
| } | |
| } | |
| Point nearestOne = new Point(-1, -1); | |
| { | |
| double dist = 1000000; | |
| for (int h = 0; h < 600; h++) { | |
| for (int w = 0; w < 600; w++) { | |
| if (copyed[h][w]) { | |
| double dx = w - one.Item1.X; | |
| double dy = h - one.Item1.Y; | |
| double tmp = Math.Sqrt(dx * dx + dy * dy); | |
| if (tmp < dist) { | |
| dist = tmp; | |
| nearestOne = new Point(w, h); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| { | |
| double dist = 0; | |
| foreach (var t in l) { | |
| if (t.Item2 > 300) continue;// 黒丸 or アミダ | |
| double dx = t.Item1.X - one.Item1.X; | |
| double dy = t.Item1.Y - one.Item1.Y; | |
| double tmp = Math.Sqrt(dx * dx + dy * dy); | |
| if (tmp > dist) { | |
| dist = tmp; | |
| LargestNum = t; | |
| } | |
| } | |
| } | |
| //右上の数字に近い点 | |
| Point nearestLast = new Point(-1, -1); | |
| { | |
| double dist = 1000000; | |
| for (int h = 0; h < 600; h++) { | |
| for (int w = 0; w < 600; w++) { | |
| if (copyed[h][w]) { | |
| double dx = w - LargestNum.Item1.X; | |
| double dy = h - LargestNum.Item1.Y; | |
| double tmp = Math.Sqrt(dx * dx + dy * dy); | |
| if (tmp < dist) { | |
| dist = tmp; | |
| nearestLast = new Point(w, h); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| Point diff = new Point(nearestLast.X - nearestOne.X, nearestLast.Y - nearestOne.Y); | |
| double angle = -Math.Atan2(diff.Y, diff.X); | |
| List<float> ret = new List<float>(); | |
| ret.Add((float)angle); | |
| for (double i = -Math.PI / 1080; i < Math.PI / 1080; i += Math.PI / 1080) { | |
| ret.Add((float)(angle + i)); | |
| } | |
| return ret; | |
| } | |
| bool gyaku(Image img, float angle) | |
| { | |
| Image rot = RotImage(img, angle); | |
| var nw_ary = convertToArray(rot); | |
| var l = getTuple(nw_ary); | |
| if (l.First().Item2 > 300) { | |
| // 上に黒丸来てる | |
| return true; | |
| } | |
| return false; | |
| } | |
| Image RotImage(Image img,float angle) | |
| { | |
| angle = (float)(angle / Math.PI * 180f); | |
| Bitmap canvas = new Bitmap(img.Width, img.Height); | |
| var g = Graphics.FromImage(canvas); | |
| g.FillRectangle(Brushes.White, new Rectangle(0, 0, img.Width, img.Height)); | |
| g.TranslateTransform(-img.Width / 2, -img.Height / 2); | |
| g.RotateTransform(angle, System.Drawing.Drawing2D.MatrixOrder.Append); | |
| g.TranslateTransform(img.Width / 2, img.Height / 2, System.Drawing.Drawing2D.MatrixOrder.Append); | |
| g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height)); | |
| g.Dispose(); | |
| return canvas; | |
| } | |
| int SolveRotate(Image img,List<bool[]> current_ary,int stage,int rank) | |
| { | |
| var angles = getAngleByConnectedComponents(img, current_ary); | |
| bool g = gyaku(img, angles[0]); | |
| if (g) { | |
| for (int i = 0; i < angles.Count; i++) angles[i] += (float)Math.PI; | |
| } | |
| foreach (var angle in angles) { | |
| using (var new_img = RotImage(img, angle)) { | |
| var nw_ary = convertToArray(new_img); | |
| int ans = Amida(nw_ary); | |
| new_img.Save(string.Format("{0}_{1}_rot.png", stage, rank)); | |
| if (ans != -1) { | |
| return ans; | |
| } | |
| } | |
| } | |
| Console.WriteLine("前回と同じ答えでごまかす"); | |
| return -1; | |
| } | |
| void main() | |
| { | |
| using (Sok sok = new Sok("203.178.132.117", 42719)) { | |
| int rank = 1,stage = 1; | |
| int last_ans = -1; | |
| while (true) { | |
| string line = sok.ReadLine(); | |
| string rank_str = string.Format("#{0}", rank); | |
| if (line != rank_str) continue; | |
| Console.WriteLine("stage : {0} , rank : {1} ", stage, rank); | |
| string png_base64 = sok.ReadLine(); | |
| sok.ReadLine(); | |
| var img = FromBase64(png_base64); | |
| if (stage < 10) { | |
| var ary = convertToArray(img,200); | |
| int ans = Amida(ary); | |
| sok.WriteLine(ans.ToString()); | |
| } | |
| else { | |
| img.Save(string.Format("{0}_{1}.png", stage, rank)); | |
| var current_ary = convertToArray(img); | |
| int index = SolveRotate(img, current_ary,stage,rank); | |
| if (index == -1) index = last_ans; | |
| last_ans = index; | |
| sok.WriteLine(index.ToString()); | |
| } | |
| rank++; | |
| if (rank > 50) { | |
| stage++; | |
| rank = 1; | |
| } | |
| } | |
| } | |
| } | |
| void rot_test() | |
| { | |
| var img = Image.FromFile("13_1.png"); | |
| var ary = convertToArray(img,200); | |
| int index = SolveRotate(img, ary,13,1); | |
| Console.WriteLine(index.ToString()); | |
| } | |
| private static void Main(string[] args) | |
| { | |
| new csharp().main(); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment