From 3531b727935306c94639a0c5b592df568e418e63 Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Fri, 1 Nov 2019 00:01:42 +0100 Subject: [PATCH 01/26] Initial commit --- .gitignore | 4 + VAR.ScreenAutomation.sln | 25 +++ VAR.ScreenAutomation/App.config | 6 + VAR.ScreenAutomation/Bots/DummyBot.cs | 28 +++ VAR.ScreenAutomation/Code/Mouse.cs | 169 ++++++++++++++++ VAR.ScreenAutomation/Code/Screenshoter.cs | 40 ++++ VAR.ScreenAutomation/Code/WindowHandling.cs | 43 +++++ .../Controls/CtrImageViewer.cs | 115 +++++++++++ VAR.ScreenAutomation/Controls/CtrOutput.cs | 182 ++++++++++++++++++ .../FrmScreenAutomation.Designer.cs | 169 ++++++++++++++++ VAR.ScreenAutomation/FrmScreenAutomation.cs | 96 +++++++++ VAR.ScreenAutomation/FrmScreenAutomation.resx | 120 ++++++++++++ .../Interfaces/IAutomationBot.cs | 11 ++ .../Interfaces/IOutputHandler.cs | 8 + VAR.ScreenAutomation/Program.cs | 19 ++ .../Properties/AssemblyInfo.cs | 36 ++++ .../Properties/Resources.Designer.cs | 71 +++++++ .../Properties/Resources.resx | 117 +++++++++++ .../Properties/Settings.Designer.cs | 30 +++ .../Properties/Settings.settings | 7 + .../VAR.ScreenAutomation.csproj | 99 ++++++++++ VAR.ScreenAutomation/app.manifest | 76 ++++++++ 22 files changed, 1471 insertions(+) create mode 100644 .gitignore create mode 100644 VAR.ScreenAutomation.sln create mode 100644 VAR.ScreenAutomation/App.config create mode 100644 VAR.ScreenAutomation/Bots/DummyBot.cs create mode 100644 VAR.ScreenAutomation/Code/Mouse.cs create mode 100644 VAR.ScreenAutomation/Code/Screenshoter.cs create mode 100644 VAR.ScreenAutomation/Code/WindowHandling.cs create mode 100644 VAR.ScreenAutomation/Controls/CtrImageViewer.cs create mode 100644 VAR.ScreenAutomation/Controls/CtrOutput.cs create mode 100644 VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs create mode 100644 VAR.ScreenAutomation/FrmScreenAutomation.cs create mode 100644 VAR.ScreenAutomation/FrmScreenAutomation.resx create mode 100644 VAR.ScreenAutomation/Interfaces/IAutomationBot.cs create mode 100644 VAR.ScreenAutomation/Interfaces/IOutputHandler.cs create mode 100644 VAR.ScreenAutomation/Program.cs create mode 100644 VAR.ScreenAutomation/Properties/AssemblyInfo.cs create mode 100644 VAR.ScreenAutomation/Properties/Resources.Designer.cs create mode 100644 VAR.ScreenAutomation/Properties/Resources.resx create mode 100644 VAR.ScreenAutomation/Properties/Settings.Designer.cs create mode 100644 VAR.ScreenAutomation/Properties/Settings.settings create mode 100644 VAR.ScreenAutomation/VAR.ScreenAutomation.csproj create mode 100644 VAR.ScreenAutomation/app.manifest diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fe5011 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/.vs/ +**/bin/** +**/obj/** +*.user diff --git a/VAR.ScreenAutomation.sln b/VAR.ScreenAutomation.sln new file mode 100644 index 0000000..8f78bf1 --- /dev/null +++ b/VAR.ScreenAutomation.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29418.71 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VAR.ScreenAutomation", "VAR.ScreenAutomation\VAR.ScreenAutomation.csproj", "{E2BE8E2A-3422-42A6-82FA-5E0CCDC42032}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E2BE8E2A-3422-42A6-82FA-5E0CCDC42032}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2BE8E2A-3422-42A6-82FA-5E0CCDC42032}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2BE8E2A-3422-42A6-82FA-5E0CCDC42032}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2BE8E2A-3422-42A6-82FA-5E0CCDC42032}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8DEFE02E-819D-4286-8378-BE842DBBBFC4} + EndGlobalSection +EndGlobal diff --git a/VAR.ScreenAutomation/App.config b/VAR.ScreenAutomation/App.config new file mode 100644 index 0000000..56efbc7 --- /dev/null +++ b/VAR.ScreenAutomation/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/VAR.ScreenAutomation/Bots/DummyBot.cs b/VAR.ScreenAutomation/Bots/DummyBot.cs new file mode 100644 index 0000000..824474f --- /dev/null +++ b/VAR.ScreenAutomation/Bots/DummyBot.cs @@ -0,0 +1,28 @@ +using System.Drawing; +using VAR.ScreenAutomation.Interfaces; + +namespace VAR.ScreenAutomation.Bots +{ + public class DummyBot : IAutomationBot + { + private int frameCount = 0; + + public void Init(IOutputHandler output) + { + frameCount = 0; + output.Clean(); + } + + public Bitmap Process(Bitmap bmpInput, IOutputHandler output) + { + frameCount++; + output.AddLine(string.Format("Frame: {0}", frameCount)); + return bmpInput; + } + + public string ResponseKeys() + { + return "{UP}"; + } + } +} diff --git a/VAR.ScreenAutomation/Code/Mouse.cs b/VAR.ScreenAutomation/Code/Mouse.cs new file mode 100644 index 0000000..2c8cfcc --- /dev/null +++ b/VAR.ScreenAutomation/Code/Mouse.cs @@ -0,0 +1,169 @@ +using System; +using System.Runtime.InteropServices; + +namespace VAR.ScreenAutomation.Code +{ + public class Mouse + { + public enum MouseButtons + { + Left, + Middle, + Right + } + + public static void SetButton(MouseButtons button, bool down) + { + INPUT input = new INPUT + { + Type = INPUT_MOUSE + }; + input.Data.Mouse.X = 0; + input.Data.Mouse.Y = 0; + if (button == MouseButtons.Left) + { + input.Data.Mouse.Flags = down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; + } + if (button == MouseButtons.Middle) + { + input.Data.Mouse.Flags = down ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP; + } + if (button == MouseButtons.Right) + { + input.Data.Mouse.Flags = down ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; + } + INPUT[] inputs = new INPUT[] { input }; + if (SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT))) == 0) + throw new Exception(); + } + + public static void Click(MouseButtons button) + { + SetButton(button, true); + System.Threading.Thread.Sleep(500); + SetButton(button, false); + } + + public static void GetPosition(out UInt32 x, out UInt32 y) + { + GetCursorPos(out POINT lpPoint); + x = lpPoint.X; + y = lpPoint.Y; + } + + public static void SetPosition(UInt32 x, UInt32 y) + { + SetCursorPos(x, y); + } + + [StructLayout(LayoutKind.Sequential)] + public struct INPUT + { + public uint Type; + public MOUSEKEYBDHARDWAREINPUT Data; + } + + public const int INPUT_MOUSE = 0; + public const int INPUT_KEYBOARD = 1; + public const int INPUT_HARDWARE = 2; + + /// + /// http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/f0e82d6e-4999-4d22-b3d3-32b25f61fb2a + /// + [StructLayout(LayoutKind.Explicit)] + public struct MOUSEKEYBDHARDWAREINPUT + { + [FieldOffset(0)] + public HARDWAREINPUT Hardware; + + [FieldOffset(0)] + public KEYBDINPUT Keyboard; + + [FieldOffset(0)] + public MOUSEINPUT Mouse; + } + + /// + /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx + /// + [StructLayout(LayoutKind.Sequential)] + public struct HARDWAREINPUT + { + public uint Msg; + public ushort ParamL; + public ushort ParamH; + } + + /// + /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx + /// + [StructLayout(LayoutKind.Sequential)] + public struct KEYBDINPUT + { + public ushort Vk; + public ushort Scan; + public uint Flags; + public uint Time; + public IntPtr ExtraInfo; + } + + /// + /// http://social.msdn.microsoft.com/forums/en-US/netfxbcl/thread/2abc6be8-c593-4686-93d2-89785232dacd + /// https://msdn.microsoft.com/es-es/library/windows/desktop/ms646273%28v=vs.85%29.aspx + /// + [StructLayout(LayoutKind.Sequential)] + public struct MOUSEINPUT + { + public int X; + public int Y; + public uint MouseData; + public uint Flags; + public uint Time; + public IntPtr ExtraInfo; + } + + public const int MOUSEEVENTD_XBUTTON1 = 0x0001; + public const int MOUSEEVENTD_XBUTTON2 = 0x0002; + + public const uint MOUSEEVENTF_ABSOLUTE = 0x8000; + public const uint MOUSEEVENTF_HWHEEL = 0x01000; + public const uint MOUSEEVENTF_MOVE = 0x0001; + public const uint MOUSEEVENTF_MOVE_NOCOALESCE = 0x2000; + public const uint MOUSEEVENTF_LEFTDOWN = 0x0002; + public const uint MOUSEEVENTF_LEFTUP = 0x0004; + public const uint MOUSEEVENTF_RIGHTDOWN = 0x0008; + public const uint MOUSEEVENTF_RIGHTUP = 0x0010; + public const uint MOUSEEVENTF_MIDDLEDOWN = 0x0020; + public const uint MOUSEEVENTF_MIDDLEUP = 0x0040; + public const uint MOUSEEVENTF_VIRTUALDESK = 0x4000; + public const uint MOUSEEVENTF_WHEEL = 0x0800; + public const uint MOUSEEVENTF_XDOWN = 0x0080; + public const uint MOUSEEVENTF_XUP = 0x0100; + + [DllImport("User32.dll")] + public static extern int SendInput(int nInputs, INPUT[] pInputs, int cbSize); + + + + /// + /// Struct representing a point. + /// + [StructLayout(LayoutKind.Sequential)] + public struct POINT + { + public UInt32 X; + public UInt32 Y; + } + + /// + /// Retrieves the cursor's position, in screen coordinates. + /// + /// See MSDN documentation for further information. + [DllImport("user32.dll")] + public static extern bool GetCursorPos(out POINT lpPoint); + + [DllImport("User32.dll")] + public static extern Boolean SetCursorPos(UInt32 X, UInt32 Y); + + } +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/Code/Screenshoter.cs b/VAR.ScreenAutomation/Code/Screenshoter.cs new file mode 100644 index 0000000..24b3b35 --- /dev/null +++ b/VAR.ScreenAutomation/Code/Screenshoter.cs @@ -0,0 +1,40 @@ +using System.Drawing; +using System.Windows.Forms; + +namespace VAR.ScreenAutomation.Code +{ + public class Screenshoter + { + public static Bitmap CaptureControl(Control ctrl, Bitmap bmp = null) + { + if (ctrl == null) { return bmp; } + Point picCapturerOrigin = ctrl.PointToScreen(new Point(0, 0)); + bmp = CaptureScreen(bmp, picCapturerOrigin.X, picCapturerOrigin.Y, ctrl.Width, ctrl.Height); + return bmp; + } + + public static Bitmap CaptureScreen(Bitmap bmp = null, int? left = null, int? top = null, int? width = null, int? height = null) + { + if (width <= 0 || height <= 0) { return bmp; } + + // Determine the size of the "virtual screen", which includes all monitors. + left = left ?? SystemInformation.VirtualScreen.Left; + top = top ?? SystemInformation.VirtualScreen.Top; + width = width ?? SystemInformation.VirtualScreen.Width; + height = height ?? SystemInformation.VirtualScreen.Height; + + // Create a bitmap of the appropriate size to receive the screenshot. + if (bmp == null || bmp?.Width != width || bmp?.Height != height) + { + bmp = new Bitmap(width ?? 0, height ?? 0); + } + + // Draw the screenshot into our bitmap. + using (Graphics g = Graphics.FromImage(bmp)) + { + g.CopyFromScreen(left ?? 0, top ?? 0, 0, 0, bmp.Size); + } + return bmp; + } + } +} diff --git a/VAR.ScreenAutomation/Code/WindowHandling.cs b/VAR.ScreenAutomation/Code/WindowHandling.cs new file mode 100644 index 0000000..33733e5 --- /dev/null +++ b/VAR.ScreenAutomation/Code/WindowHandling.cs @@ -0,0 +1,43 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace VAR.ScreenAutomation.Code +{ + public class WindowHandling + { + private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); + private const UInt32 SWP_NOSIZE = 0x0001; + private const UInt32 SWP_NOMOVE = 0x0002; + private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE; + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + + public static void WindowSetTopLevel(Form form) + { + SetWindowPos(form.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS); + } + + public static bool ApplicationIsActivated() + { + var activatedHandle = GetForegroundWindow(); + if (activatedHandle == IntPtr.Zero) + { + return false; + } + + var procId = Process.GetCurrentProcess().Id; + GetWindowThreadProcessId(activatedHandle, out int activeProcId); + return activeProcId == procId; + } + + [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] + private static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern int GetWindowThreadProcessId(IntPtr handle, out int processId); + } +} diff --git a/VAR.ScreenAutomation/Controls/CtrImageViewer.cs b/VAR.ScreenAutomation/Controls/CtrImageViewer.cs new file mode 100644 index 0000000..361a5eb --- /dev/null +++ b/VAR.ScreenAutomation/Controls/CtrImageViewer.cs @@ -0,0 +1,115 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace VAR.ScreenAutomation.Controls +{ + public class CtrImageViewer : PictureBox + { + #region Declarations + + private Image _imageShow = null; + + #endregion + + #region Properties + + public Image ImageShow + { + get { return _imageShow; } + set + { + lock (this) + { + _imageShow = value; + Invalidate(); + } + } + } + + #endregion + + #region Control life cycle + + public CtrImageViewer() + { + BackColor = Color.Black; + } + + protected override void OnPaint(PaintEventArgs pe) + { + base.OnPaint(pe); + Redraw(pe.Graphics); + } + + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + //Redraw(null); + this.Invalidate(); + } + + #endregion + + #region Private methods + + private void Redraw(Graphics graph) + { + if (_imageShow == null) + { + return; + } + lock (_imageShow) + { + if (graph == null) + { + graph = this.CreateGraphics(); + } + + // Calcular dimensiones a dibujar y centrar + int imgDrawWidth; + int imgDrawHeight; + float imgDrawX = 0; + float imgDrawY = 0; + float relation = (float)_imageShow.Width / (float)_imageShow.Height; + if (relation > 0) + { + // Imagen mas ancha que alta + imgDrawHeight = (int)(Width / relation); + if (imgDrawHeight > Height) + { + imgDrawHeight = Height; + imgDrawWidth = (int)(Height * relation); + imgDrawX = ((Width - imgDrawWidth) / 2.0f); + } + else + { + imgDrawWidth = Width; + imgDrawY = ((Height - imgDrawHeight) / 2.0f); + } + } + else + { + // Imagen mas alta que ancha + imgDrawWidth = (int)(Width * relation); + if (imgDrawWidth > Width) + { + imgDrawWidth = Width; + imgDrawHeight = (int)(Height / relation); + imgDrawY = ((Height - imgDrawHeight) / 2.0f); + } + else + { + imgDrawHeight = Height; + imgDrawX = ((Width - imgDrawWidth) / 2.0f); + } + } + + graph.DrawImage(_imageShow, imgDrawX, imgDrawY, imgDrawWidth, imgDrawHeight); + } + } + + #endregion + + } +} diff --git a/VAR.ScreenAutomation/Controls/CtrOutput.cs b/VAR.ScreenAutomation/Controls/CtrOutput.cs new file mode 100644 index 0000000..d64ce96 --- /dev/null +++ b/VAR.ScreenAutomation/Controls/CtrOutput.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Forms; +using VAR.ScreenAutomation.Interfaces; + +namespace VAR.ScreenAutomation.Controls +{ + public class CtrOutput : Control, IOutputHandler + { + private ListBox _listBox; + + private Timer _timer; + + private class OutputItem + { + public string Text { get; set; } + public object Data { get; set; } + public override string ToString() + { + return Text; + } + } + + public new event EventHandler DoubleClick; + + public CtrOutput() + { + InitializeControls(); + } + + private void InitializeControls() + { + _listBox = new ListBox + { + Dock = DockStyle.Fill, + FormattingEnabled = true, + Font = new System.Drawing.Font("Consolas", 9), + BackColor = Color.Black, + ForeColor = Color.Gray, + SelectionMode = SelectionMode.MultiExtended, + }; + _listBox.MouseDoubleClick += ListBox_MouseDoubleClick; + _listBox.KeyDown += ListBox_KeyDown; + Controls.Add(_listBox); + + _timer = new Timer + { + Interval = 100, + Enabled = true + }; + _timer.Tick += Timer_Tick; + + Disposed += CtrOutput_Disposed; + } + + private void CtrOutput_Disposed(object sender, EventArgs e) + { + _timer.Stop(); + _timer.Enabled = false; + } + + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + if ((keyData & Keys.Control) == Keys.Control && (keyData & Keys.C) == Keys.C) + { + CopyToClipboard(); + return true; + } + return base.ProcessCmdKey(ref msg, keyData); + } + + private void ListBox_KeyDown(object sender, KeyEventArgs e) + { + if (e.Control && e.KeyCode == Keys.C) + { + CopyToClipboard(); + } + } + + private void CopyToClipboard() + { + StringBuilder sbText = new StringBuilder(); + foreach (OutputItem item in _listBox.SelectedItems) + { + sbText.AppendLine(item.Text); + } + if (sbText.Length > 0) + { + Clipboard.SetText(sbText.ToString()); + } + } + + private void ListBox_MouseDoubleClick(object sender, MouseEventArgs e) + { + DoubleClick?.Invoke(sender, e); + } + + private void Timer_Tick(object sender, EventArgs e) + { + if (_updated) + { + UpdatePosition(); + } + } + + private bool _updated = false; + private readonly List _pendingOutput = new List(); + + private void UpdatePosition() + { + lock (_pendingOutput) + { + EnableRepaint(new HandleRef(_listBox, _listBox.Handle), false); + _listBox.SuspendLayout(); + foreach (OutputItem item in _pendingOutput) + { + _listBox.Items.Add(item); + } + _pendingOutput.Clear(); + _listBox.ResumeLayout(); + + int visibleItems = _listBox.ClientSize.Height / _listBox.ItemHeight; + _listBox.TopIndex = Math.Max(_listBox.Items.Count - visibleItems + 1, 0); + _updated = false; + EnableRepaint(new HandleRef(_listBox, _listBox.Handle), true); + _listBox.Invalidate(); + } + } + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)] + private static extern IntPtr SendMessage(HandleRef hWnd, Int32 Msg, IntPtr wParam, IntPtr lParam); + + private static void EnableRepaint(HandleRef handle, bool enable) + { + const int WM_SETREDRAW = 0x000B; + SendMessage(handle, WM_SETREDRAW, new IntPtr(enable ? 1 : 0), IntPtr.Zero); + } + + public void Clean() + { + if (_listBox.InvokeRequired) + { + _listBox.Invoke((MethodInvoker)(() => + { + _listBox.Items.Clear(); + _updated = true; + })); + } + else + { + _listBox.Items.Clear(); + _updated = true; + } + } + + public void AddLine(string line, object data = null) + { + lock (_pendingOutput) + { + _pendingOutput.Add(new OutputItem { Text = line, Data = data, }); + _updated = true; + } + } + + public string GetCurrentText() + { + if (_listBox.SelectedItems.Count == 0) { return null; } + OutputItem item = (OutputItem)_listBox.SelectedItems[0]; + return item?.Text; + } + + public object GetCurrentData() + { + if (_listBox.SelectedItems.Count == 0) { return null; } + OutputItem item = (OutputItem)_listBox.SelectedItems[0]; + return item?.Data; + } + } +} diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs b/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs new file mode 100644 index 0000000..ad40994 --- /dev/null +++ b/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs @@ -0,0 +1,169 @@ +namespace VAR.ScreenAutomation +{ + partial class FrmScreenAutomation + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.picCapturer = new System.Windows.Forms.PictureBox(); + this.splitMain = new System.Windows.Forms.SplitContainer(); + this.splitOutput = new System.Windows.Forms.SplitContainer(); + this.picPreview = new VAR.ScreenAutomation.Controls.CtrImageViewer(); + this.btnStartEnd = new System.Windows.Forms.Button(); + this.ctrOutput = new VAR.ScreenAutomation.Controls.CtrOutput(); + ((System.ComponentModel.ISupportInitialize)(this.picCapturer)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.splitMain)).BeginInit(); + this.splitMain.Panel1.SuspendLayout(); + this.splitMain.Panel2.SuspendLayout(); + this.splitMain.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.splitOutput)).BeginInit(); + this.splitOutput.Panel1.SuspendLayout(); + this.splitOutput.Panel2.SuspendLayout(); + this.splitOutput.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picPreview)).BeginInit(); + this.SuspendLayout(); + // + // picCapturer + // + this.picCapturer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.picCapturer.Location = new System.Drawing.Point(4, 4); + this.picCapturer.Margin = new System.Windows.Forms.Padding(4); + this.picCapturer.Name = "picCapturer"; + this.picCapturer.Padding = new System.Windows.Forms.Padding(10); + this.picCapturer.Size = new System.Drawing.Size(424, 799); + this.picCapturer.TabIndex = 0; + this.picCapturer.TabStop = false; + // + // splitMain + // + this.splitMain.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitMain.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; + this.splitMain.Location = new System.Drawing.Point(0, 0); + this.splitMain.Margin = new System.Windows.Forms.Padding(10); + this.splitMain.Name = "splitMain"; + // + // splitMain.Panel1 + // + this.splitMain.Panel1.Controls.Add(this.splitOutput); + // + // splitMain.Panel2 + // + this.splitMain.Panel2.Controls.Add(this.picCapturer); + this.splitMain.Size = new System.Drawing.Size(754, 816); + this.splitMain.SplitterDistance = 309; + this.splitMain.TabIndex = 3; + // + // splitOutput + // + this.splitOutput.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitOutput.Location = new System.Drawing.Point(0, 0); + this.splitOutput.Name = "splitOutput"; + this.splitOutput.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // splitOutput.Panel1 + // + this.splitOutput.Panel1.Controls.Add(this.picPreview); + // + // splitOutput.Panel2 + // + this.splitOutput.Panel2.Controls.Add(this.btnStartEnd); + this.splitOutput.Panel2.Controls.Add(this.ctrOutput); + this.splitOutput.Size = new System.Drawing.Size(309, 816); + this.splitOutput.SplitterDistance = 371; + this.splitOutput.TabIndex = 4; + // + // picPreview + // + this.picPreview.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.picPreview.BackColor = System.Drawing.Color.Black; + this.picPreview.ImageShow = null; + this.picPreview.Location = new System.Drawing.Point(12, 12); + this.picPreview.Name = "picPreview"; + this.picPreview.Size = new System.Drawing.Size(294, 356); + this.picPreview.TabIndex = 1; + this.picPreview.TabStop = false; + // + // btnStartEnd + // + this.btnStartEnd.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btnStartEnd.Location = new System.Drawing.Point(12, 3); + this.btnStartEnd.Name = "btnStartEnd"; + this.btnStartEnd.Size = new System.Drawing.Size(294, 39); + this.btnStartEnd.TabIndex = 3; + this.btnStartEnd.Text = "Start"; + this.btnStartEnd.UseVisualStyleBackColor = true; + this.btnStartEnd.Click += new System.EventHandler(this.BtnStartEnd_Click); + // + // ctrOutput + // + this.ctrOutput.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.ctrOutput.Location = new System.Drawing.Point(12, 48); + this.ctrOutput.Name = "ctrOutput"; + this.ctrOutput.Size = new System.Drawing.Size(294, 380); + this.ctrOutput.TabIndex = 2; + this.ctrOutput.Text = "ctrOutput1"; + // + // FrmScreenAutomation + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(754, 816); + this.Controls.Add(this.splitMain); + this.Margin = new System.Windows.Forms.Padding(4); + this.Name = "FrmScreenAutomation"; + this.Text = "ScreenAutomation"; + this.Load += new System.EventHandler(this.FrmScreenAutomation_Load); + ((System.ComponentModel.ISupportInitialize)(this.picCapturer)).EndInit(); + this.splitMain.Panel1.ResumeLayout(false); + this.splitMain.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitMain)).EndInit(); + this.splitMain.ResumeLayout(false); + this.splitOutput.Panel1.ResumeLayout(false); + this.splitOutput.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitOutput)).EndInit(); + this.splitOutput.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.picPreview)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.PictureBox picCapturer; + private Controls.CtrImageViewer picPreview; + private Controls.CtrOutput ctrOutput; + private System.Windows.Forms.SplitContainer splitMain; + private System.Windows.Forms.SplitContainer splitOutput; + private System.Windows.Forms.Button btnStartEnd; + } +} + diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.cs b/VAR.ScreenAutomation/FrmScreenAutomation.cs new file mode 100644 index 0000000..96ef5d8 --- /dev/null +++ b/VAR.ScreenAutomation/FrmScreenAutomation.cs @@ -0,0 +1,96 @@ +using System; +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; +using VAR.ScreenAutomation.Bots; +using VAR.ScreenAutomation.Code; +using VAR.ScreenAutomation.Interfaces; + +namespace VAR.ScreenAutomation +{ + public partial class FrmScreenAutomation : Form + { + private bool _running = false; + private IAutomationBot _automationBot = new DummyBot(); + + private Timer timTicker; + private Bitmap bmpScreen = null; + + public FrmScreenAutomation() + { + AutoScaleMode = AutoScaleMode.None; + AutoScaleDimensions = new SizeF(1, 1); + InitializeComponent(); + } + + private void FrmScreenAutomation_Load(object sender, EventArgs e) + { + SetStyle(ControlStyles.UserPaint, true); + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + SetStyle(ControlStyles.SupportsTransparentBackColor, true); + TransparencyKey = Color.LimeGreen; + picCapturer.BackColor = Color.LimeGreen; + + if (components == null) { components = new Container(); } + timTicker = new Timer(components) + { + Interval = 16, + }; + timTicker.Tick += TimTicker_Tick; + timTicker.Enabled = true; + timTicker.Start(); + + WindowHandling.WindowSetTopLevel(this); + } + + private void TimTicker_Tick(object sender, EventArgs e) + { + timTicker.Stop(); + + bmpScreen = Screenshoter.CaptureControl(picCapturer, bmpScreen); + + if (_automationBot != null && _running) + { + bmpScreen = _automationBot.Process(bmpScreen, ctrOutput); + string responseKeys = _automationBot.ResponseKeys(); + if (string.IsNullOrEmpty(responseKeys) == false && WindowHandling.ApplicationIsActivated() == false) + { + SendKeys.Send(responseKeys); + } + } + picPreview.ImageShow = bmpScreen; + + timTicker.Start(); + } + + private void BtnStartEnd_Click(object sender, EventArgs e) + { + if (_running) + { + End(); + } + else + { + Start(); + } + } + + private void Start() + { + if (_running) { return; } + _running = true; + btnStartEnd.Text = "End"; + _automationBot?.Init(ctrOutput); + Point pointCapturerCenter = picCapturer.PointToScreen(new Point(picCapturer.Width / 2, picCapturer.Height / 2)); + Mouse.SetPosition((uint)pointCapturerCenter.X, (uint)pointCapturerCenter.Y); + Mouse.Click(Mouse.MouseButtons.Left); + } + + private void End() + { + if (_running == false) { return; } + _running = false; + btnStartEnd.Text = "Start"; + } + } +} diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.resx b/VAR.ScreenAutomation/FrmScreenAutomation.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/VAR.ScreenAutomation/FrmScreenAutomation.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/VAR.ScreenAutomation/Interfaces/IAutomationBot.cs b/VAR.ScreenAutomation/Interfaces/IAutomationBot.cs new file mode 100644 index 0000000..674788c --- /dev/null +++ b/VAR.ScreenAutomation/Interfaces/IAutomationBot.cs @@ -0,0 +1,11 @@ +using System.Drawing; + +namespace VAR.ScreenAutomation.Interfaces +{ + public interface IAutomationBot + { + void Init(IOutputHandler output); + Bitmap Process(Bitmap bmpInput, IOutputHandler output); + string ResponseKeys(); + } +} diff --git a/VAR.ScreenAutomation/Interfaces/IOutputHandler.cs b/VAR.ScreenAutomation/Interfaces/IOutputHandler.cs new file mode 100644 index 0000000..b86c242 --- /dev/null +++ b/VAR.ScreenAutomation/Interfaces/IOutputHandler.cs @@ -0,0 +1,8 @@ +namespace VAR.ScreenAutomation.Interfaces +{ + public interface IOutputHandler + { + void Clean(); + void AddLine(string line, object data = null); + } +} diff --git a/VAR.ScreenAutomation/Program.cs b/VAR.ScreenAutomation/Program.cs new file mode 100644 index 0000000..308f05b --- /dev/null +++ b/VAR.ScreenAutomation/Program.cs @@ -0,0 +1,19 @@ +using System; +using System.Windows.Forms; + +namespace VAR.ScreenAutomation +{ + internal static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + private static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new FrmScreenAutomation()); + } + } +} diff --git a/VAR.ScreenAutomation/Properties/AssemblyInfo.cs b/VAR.ScreenAutomation/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1f625a3 --- /dev/null +++ b/VAR.ScreenAutomation/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("VAR.ScreenAutomation")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("VAR.ScreenAutomation")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e2be8e2a-3422-42a6-82fa-5e0ccdc42032")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/VAR.ScreenAutomation/Properties/Resources.Designer.cs b/VAR.ScreenAutomation/Properties/Resources.Designer.cs new file mode 100644 index 0000000..106fa66 --- /dev/null +++ b/VAR.ScreenAutomation/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace VAR.ScreenAutomation.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("VAR.ScreenAutomation.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/VAR.ScreenAutomation/Properties/Resources.resx b/VAR.ScreenAutomation/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/VAR.ScreenAutomation/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/VAR.ScreenAutomation/Properties/Settings.Designer.cs b/VAR.ScreenAutomation/Properties/Settings.Designer.cs new file mode 100644 index 0000000..15e5d03 --- /dev/null +++ b/VAR.ScreenAutomation/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace VAR.ScreenAutomation.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/VAR.ScreenAutomation/Properties/Settings.settings b/VAR.ScreenAutomation/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/VAR.ScreenAutomation/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj b/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj new file mode 100644 index 0000000..2fb3d62 --- /dev/null +++ b/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj @@ -0,0 +1,99 @@ + + + + + Debug + AnyCPU + {E2BE8E2A-3422-42A6-82FA-5E0CCDC42032} + WinExe + VAR.ScreenAutomation + VAR.ScreenAutomation + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + app.manifest + + + + + + + + + + + + + + + + + + + + + Component + + + Component + + + Form + + + FrmScreenAutomation.cs + + + + + + + FrmScreenAutomation.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/VAR.ScreenAutomation/app.manifest b/VAR.ScreenAutomation/app.manifest new file mode 100644 index 0000000..648dc43 --- /dev/null +++ b/VAR.ScreenAutomation/app.manifest @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + From 437366fd678396cdd0e3c205038536576f9cde0c Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Fri, 1 Nov 2019 00:27:57 +0100 Subject: [PATCH 02/26] Implement AutomationBotFactory and UI for Bot selection. --- VAR.ScreenAutomation/Bots/DummyBot.cs | 2 + .../Code/AutomationBotFactory.cs | 63 +++++++++++++++++++ .../FrmScreenAutomation.Designer.cs | 56 +++++++++++------ VAR.ScreenAutomation/FrmScreenAutomation.cs | 9 ++- .../Interfaces/IAutomationBot.cs | 1 + .../VAR.ScreenAutomation.csproj | 1 + 6 files changed, 110 insertions(+), 22 deletions(-) create mode 100644 VAR.ScreenAutomation/Code/AutomationBotFactory.cs diff --git a/VAR.ScreenAutomation/Bots/DummyBot.cs b/VAR.ScreenAutomation/Bots/DummyBot.cs index 824474f..e2c26e7 100644 --- a/VAR.ScreenAutomation/Bots/DummyBot.cs +++ b/VAR.ScreenAutomation/Bots/DummyBot.cs @@ -7,6 +7,8 @@ namespace VAR.ScreenAutomation.Bots { private int frameCount = 0; + public string Name => "Dummy"; + public void Init(IOutputHandler output) { frameCount = 0; diff --git a/VAR.ScreenAutomation/Code/AutomationBotFactory.cs b/VAR.ScreenAutomation/Code/AutomationBotFactory.cs new file mode 100644 index 0000000..2d4277a --- /dev/null +++ b/VAR.ScreenAutomation/Code/AutomationBotFactory.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using VAR.ScreenAutomation.Bots; +using VAR.ScreenAutomation.Interfaces; + +namespace VAR.ScreenAutomation.Code +{ + public class AutomationBotFactory + { + private static Dictionary _dictAutomationBots = null; + + private static Dictionary GetDict() + { + if (_dictAutomationBots != null) + { + return _dictAutomationBots; + } + + Type iAutomationBot = typeof(IAutomationBot); + IEnumerable automationBotTypes = AppDomain.CurrentDomain + .GetAssemblies() + .SelectMany(x => x.GetTypes()) + .Where(x => + x.IsAbstract == false && + x.IsInterface == false && + iAutomationBot.IsAssignableFrom(x) && + true); + _dictAutomationBots = automationBotTypes.ToDictionary(t => + { + IAutomationBot automationBot = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(t) as IAutomationBot; + return automationBot.Name; + }); + + return _dictAutomationBots; + } + + public static object[] GetAllAutomationBots() + { + Dictionary dict = GetDict(); + string[] allAutomationBots = dict.Select(p => p.Key).ToArray(); + return allAutomationBots; + } + + public static IAutomationBot CreateFromName(string name) + { + Dictionary dict = GetDict(); + if (string.IsNullOrEmpty(name)) + { + return new DummyBot(); + } + if (dict.ContainsKey(name) == false) + { + throw new NotImplementedException(string.Format("Can't create IAutomationBot with this name: {0}", name)); + } + Type proxyCmdExecutorType = dict[name]; + + IAutomationBot automationBot = Activator.CreateInstance(proxyCmdExecutorType) as IAutomationBot; + + return automationBot; + } + } +} diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs b/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs index ad40994..ed22c2d 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs +++ b/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs @@ -31,8 +31,9 @@ this.picCapturer = new System.Windows.Forms.PictureBox(); this.splitMain = new System.Windows.Forms.SplitContainer(); this.splitOutput = new System.Windows.Forms.SplitContainer(); - this.picPreview = new VAR.ScreenAutomation.Controls.CtrImageViewer(); + this.ddlAutomationBot = new System.Windows.Forms.ComboBox(); this.btnStartEnd = new System.Windows.Forms.Button(); + this.picPreview = new VAR.ScreenAutomation.Controls.CtrImageViewer(); this.ctrOutput = new VAR.ScreenAutomation.Controls.CtrOutput(); ((System.ComponentModel.ISupportInitialize)(this.picCapturer)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.splitMain)).BeginInit(); @@ -48,8 +49,8 @@ // // picCapturer // - this.picCapturer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) + this.picCapturer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.picCapturer.Location = new System.Drawing.Point(4, 4); this.picCapturer.Margin = new System.Windows.Forms.Padding(4); @@ -91,16 +92,41 @@ // // splitOutput.Panel2 // + this.splitOutput.Panel2.Controls.Add(this.ddlAutomationBot); this.splitOutput.Panel2.Controls.Add(this.btnStartEnd); this.splitOutput.Panel2.Controls.Add(this.ctrOutput); this.splitOutput.Size = new System.Drawing.Size(309, 816); this.splitOutput.SplitterDistance = 371; this.splitOutput.TabIndex = 4; // + // ddlAutomationBot + // + this.ddlAutomationBot.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.ddlAutomationBot.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.ddlAutomationBot.FormattingEnabled = true; + this.ddlAutomationBot.Location = new System.Drawing.Point(13, 4); + this.ddlAutomationBot.Name = "ddlAutomationBot"; + this.ddlAutomationBot.Size = new System.Drawing.Size(293, 24); + this.ddlAutomationBot.TabIndex = 4; + // + // btnStartEnd + // + this.btnStartEnd.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.btnStartEnd.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btnStartEnd.Location = new System.Drawing.Point(15, 34); + this.btnStartEnd.Name = "btnStartEnd"; + this.btnStartEnd.Size = new System.Drawing.Size(294, 39); + this.btnStartEnd.TabIndex = 3; + this.btnStartEnd.Text = "Start"; + this.btnStartEnd.UseVisualStyleBackColor = true; + this.btnStartEnd.Click += new System.EventHandler(this.BtnStartEnd_Click); + // // picPreview // - this.picPreview.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) + this.picPreview.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.picPreview.BackColor = System.Drawing.Color.Black; this.picPreview.ImageShow = null; @@ -110,25 +136,14 @@ this.picPreview.TabIndex = 1; this.picPreview.TabStop = false; // - // btnStartEnd - // - this.btnStartEnd.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.btnStartEnd.Location = new System.Drawing.Point(12, 3); - this.btnStartEnd.Name = "btnStartEnd"; - this.btnStartEnd.Size = new System.Drawing.Size(294, 39); - this.btnStartEnd.TabIndex = 3; - this.btnStartEnd.Text = "Start"; - this.btnStartEnd.UseVisualStyleBackColor = true; - this.btnStartEnd.Click += new System.EventHandler(this.BtnStartEnd_Click); - // // ctrOutput // - this.ctrOutput.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) + this.ctrOutput.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.ctrOutput.Location = new System.Drawing.Point(12, 48); + this.ctrOutput.Location = new System.Drawing.Point(12, 79); this.ctrOutput.Name = "ctrOutput"; - this.ctrOutput.Size = new System.Drawing.Size(294, 380); + this.ctrOutput.Size = new System.Drawing.Size(294, 349); this.ctrOutput.TabIndex = 2; this.ctrOutput.Text = "ctrOutput1"; // @@ -164,6 +179,7 @@ private System.Windows.Forms.SplitContainer splitMain; private System.Windows.Forms.SplitContainer splitOutput; private System.Windows.Forms.Button btnStartEnd; + private System.Windows.Forms.ComboBox ddlAutomationBot; } } diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.cs b/VAR.ScreenAutomation/FrmScreenAutomation.cs index 96ef5d8..7520253 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.cs +++ b/VAR.ScreenAutomation/FrmScreenAutomation.cs @@ -2,7 +2,6 @@ using System.ComponentModel; using System.Drawing; using System.Windows.Forms; -using VAR.ScreenAutomation.Bots; using VAR.ScreenAutomation.Code; using VAR.ScreenAutomation.Interfaces; @@ -11,7 +10,7 @@ namespace VAR.ScreenAutomation public partial class FrmScreenAutomation : Form { private bool _running = false; - private IAutomationBot _automationBot = new DummyBot(); + private IAutomationBot _automationBot = null; private Timer timTicker; private Bitmap bmpScreen = null; @@ -31,6 +30,11 @@ namespace VAR.ScreenAutomation TransparencyKey = Color.LimeGreen; picCapturer.BackColor = Color.LimeGreen; + + ddlAutomationBot.Items.AddRange(AutomationBotFactory.GetAllAutomationBots()); + ddlAutomationBot.SelectedIndex = 0; + + if (components == null) { components = new Container(); } timTicker = new Timer(components) { @@ -80,6 +84,7 @@ namespace VAR.ScreenAutomation if (_running) { return; } _running = true; btnStartEnd.Text = "End"; + _automationBot = AutomationBotFactory.CreateFromName((string)ddlAutomationBot.SelectedItem); _automationBot?.Init(ctrOutput); Point pointCapturerCenter = picCapturer.PointToScreen(new Point(picCapturer.Width / 2, picCapturer.Height / 2)); Mouse.SetPosition((uint)pointCapturerCenter.X, (uint)pointCapturerCenter.Y); diff --git a/VAR.ScreenAutomation/Interfaces/IAutomationBot.cs b/VAR.ScreenAutomation/Interfaces/IAutomationBot.cs index 674788c..1aea4a6 100644 --- a/VAR.ScreenAutomation/Interfaces/IAutomationBot.cs +++ b/VAR.ScreenAutomation/Interfaces/IAutomationBot.cs @@ -4,6 +4,7 @@ namespace VAR.ScreenAutomation.Interfaces { public interface IAutomationBot { + string Name { get; } void Init(IOutputHandler output); Bitmap Process(Bitmap bmpInput, IOutputHandler output); string ResponseKeys(); diff --git a/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj b/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj index 2fb3d62..faddfd1 100644 --- a/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj +++ b/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj @@ -50,6 +50,7 @@ + From d3e4e53c1b0391df2b35701b7ad81513a30a347d Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Fri, 1 Nov 2019 02:37:36 +0100 Subject: [PATCH 03/26] FrmScreenAutomation: Process bots always. --- .../FrmScreenAutomation.Designer.cs | 1 + VAR.ScreenAutomation/FrmScreenAutomation.cs | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs b/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs index ed22c2d..62c443f 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs +++ b/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs @@ -109,6 +109,7 @@ this.ddlAutomationBot.Name = "ddlAutomationBot"; this.ddlAutomationBot.Size = new System.Drawing.Size(293, 24); this.ddlAutomationBot.TabIndex = 4; + this.ddlAutomationBot.SelectedIndexChanged += new System.EventHandler(this.DdlAutomationBot_SelectedIndexChanged); // // btnStartEnd // diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.cs b/VAR.ScreenAutomation/FrmScreenAutomation.cs index 7520253..6156ae8 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.cs +++ b/VAR.ScreenAutomation/FrmScreenAutomation.cs @@ -33,6 +33,8 @@ namespace VAR.ScreenAutomation ddlAutomationBot.Items.AddRange(AutomationBotFactory.GetAllAutomationBots()); ddlAutomationBot.SelectedIndex = 0; + _automationBot = AutomationBotFactory.CreateFromName((string)ddlAutomationBot.SelectedItem); + _automationBot?.Init(ctrOutput); if (components == null) { components = new Container(); } @@ -53,13 +55,16 @@ namespace VAR.ScreenAutomation bmpScreen = Screenshoter.CaptureControl(picCapturer, bmpScreen); - if (_automationBot != null && _running) + if (_automationBot != null) { bmpScreen = _automationBot.Process(bmpScreen, ctrOutput); - string responseKeys = _automationBot.ResponseKeys(); - if (string.IsNullOrEmpty(responseKeys) == false && WindowHandling.ApplicationIsActivated() == false) + if (_running) { - SendKeys.Send(responseKeys); + string responseKeys = _automationBot.ResponseKeys(); + if (string.IsNullOrEmpty(responseKeys) == false && WindowHandling.ApplicationIsActivated() == false) + { + SendKeys.Send(responseKeys); + } } } picPreview.ImageShow = bmpScreen; @@ -97,5 +102,11 @@ namespace VAR.ScreenAutomation _running = false; btnStartEnd.Text = "Start"; } + + private void DdlAutomationBot_SelectedIndexChanged(object sender, EventArgs e) + { + _automationBot = AutomationBotFactory.CreateFromName((string)ddlAutomationBot.SelectedItem); + _automationBot?.Init(ctrOutput); + } } } From 1a46040210390410c47b699ff48e78413a7207ca Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Fri, 1 Nov 2019 02:38:28 +0100 Subject: [PATCH 04/26] DummyBot: Even more dummy. --- VAR.ScreenAutomation/Bots/DummyBot.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/VAR.ScreenAutomation/Bots/DummyBot.cs b/VAR.ScreenAutomation/Bots/DummyBot.cs index e2c26e7..5e43325 100644 --- a/VAR.ScreenAutomation/Bots/DummyBot.cs +++ b/VAR.ScreenAutomation/Bots/DummyBot.cs @@ -5,26 +5,23 @@ namespace VAR.ScreenAutomation.Bots { public class DummyBot : IAutomationBot { - private int frameCount = 0; - public string Name => "Dummy"; public void Init(IOutputHandler output) { - frameCount = 0; output.Clean(); } public Bitmap Process(Bitmap bmpInput, IOutputHandler output) { - frameCount++; - output.AddLine(string.Format("Frame: {0}", frameCount)); return bmpInput; } public string ResponseKeys() { - return "{UP}"; + // https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.sendkeys?view=netframework-4.8 + //return "{UP}"; + return string.Empty; } } } From 71ab81e11af26ee3c09edd408e1908ca6b1541ae Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Sat, 2 Nov 2019 00:55:10 +0100 Subject: [PATCH 05/26] FrmScreenAutomation: Adjust timer to 10FPS. --- VAR.ScreenAutomation/FrmScreenAutomation.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.cs b/VAR.ScreenAutomation/FrmScreenAutomation.cs index 6156ae8..400b1ab 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.cs +++ b/VAR.ScreenAutomation/FrmScreenAutomation.cs @@ -40,7 +40,7 @@ namespace VAR.ScreenAutomation if (components == null) { components = new Container(); } timTicker = new Timer(components) { - Interval = 16, + Interval = 100, }; timTicker.Tick += TimTicker_Tick; timTicker.Enabled = true; @@ -51,6 +51,7 @@ namespace VAR.ScreenAutomation private void TimTicker_Tick(object sender, EventArgs e) { + timTicker.Enabled = false; timTicker.Stop(); bmpScreen = Screenshoter.CaptureControl(picCapturer, bmpScreen); @@ -69,6 +70,7 @@ namespace VAR.ScreenAutomation } picPreview.ImageShow = bmpScreen; + timTicker.Enabled = true; timTicker.Start(); } From d7116687f58b313bd46895e6f040f408994fe231 Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Sat, 2 Nov 2019 22:33:31 +0100 Subject: [PATCH 06/26] FrmScreenAutomation: Variable FPS selector. --- .../FrmScreenAutomation.Designer.cs | 60 ++++++++++++++----- VAR.ScreenAutomation/FrmScreenAutomation.cs | 3 +- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs b/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs index 62c443f..9b2f5f8 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs +++ b/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs @@ -33,6 +33,7 @@ this.splitOutput = new System.Windows.Forms.SplitContainer(); this.ddlAutomationBot = new System.Windows.Forms.ComboBox(); this.btnStartEnd = new System.Windows.Forms.Button(); + this.numFPS = new System.Windows.Forms.NumericUpDown(); this.picPreview = new VAR.ScreenAutomation.Controls.CtrImageViewer(); this.ctrOutput = new VAR.ScreenAutomation.Controls.CtrOutput(); ((System.ComponentModel.ISupportInitialize)(this.picCapturer)).BeginInit(); @@ -44,19 +45,20 @@ this.splitOutput.Panel1.SuspendLayout(); this.splitOutput.Panel2.SuspendLayout(); this.splitOutput.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numFPS)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.picPreview)).BeginInit(); this.SuspendLayout(); // // picCapturer // - this.picCapturer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) + this.picCapturer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.picCapturer.Location = new System.Drawing.Point(4, 4); this.picCapturer.Margin = new System.Windows.Forms.Padding(4); this.picCapturer.Name = "picCapturer"; this.picCapturer.Padding = new System.Windows.Forms.Padding(10); - this.picCapturer.Size = new System.Drawing.Size(424, 799); + this.picCapturer.Size = new System.Drawing.Size(501, 799); this.picCapturer.TabIndex = 0; this.picCapturer.TabStop = false; // @@ -76,7 +78,7 @@ // this.splitMain.Panel2.Controls.Add(this.picCapturer); this.splitMain.Size = new System.Drawing.Size(754, 816); - this.splitMain.SplitterDistance = 309; + this.splitMain.SplitterDistance = 232; this.splitMain.TabIndex = 3; // // splitOutput @@ -92,59 +94,83 @@ // // splitOutput.Panel2 // + this.splitOutput.Panel2.Controls.Add(this.numFPS); this.splitOutput.Panel2.Controls.Add(this.ddlAutomationBot); this.splitOutput.Panel2.Controls.Add(this.btnStartEnd); this.splitOutput.Panel2.Controls.Add(this.ctrOutput); - this.splitOutput.Size = new System.Drawing.Size(309, 816); - this.splitOutput.SplitterDistance = 371; + this.splitOutput.Size = new System.Drawing.Size(232, 816); + this.splitOutput.SplitterDistance = 283; this.splitOutput.TabIndex = 4; // // ddlAutomationBot // - this.ddlAutomationBot.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.ddlAutomationBot.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ddlAutomationBot.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.ddlAutomationBot.FormattingEnabled = true; this.ddlAutomationBot.Location = new System.Drawing.Point(13, 4); this.ddlAutomationBot.Name = "ddlAutomationBot"; - this.ddlAutomationBot.Size = new System.Drawing.Size(293, 24); + this.ddlAutomationBot.Size = new System.Drawing.Size(216, 24); this.ddlAutomationBot.TabIndex = 4; this.ddlAutomationBot.SelectedIndexChanged += new System.EventHandler(this.DdlAutomationBot_SelectedIndexChanged); // // btnStartEnd // - this.btnStartEnd.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.btnStartEnd.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.btnStartEnd.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btnStartEnd.Location = new System.Drawing.Point(15, 34); this.btnStartEnd.Name = "btnStartEnd"; - this.btnStartEnd.Size = new System.Drawing.Size(294, 39); + this.btnStartEnd.Size = new System.Drawing.Size(149, 39); this.btnStartEnd.TabIndex = 3; this.btnStartEnd.Text = "Start"; this.btnStartEnd.UseVisualStyleBackColor = true; this.btnStartEnd.Click += new System.EventHandler(this.BtnStartEnd_Click); // + // numFPS + // + this.numFPS.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.numFPS.Location = new System.Drawing.Point(170, 50); + this.numFPS.Maximum = new decimal(new int[] { + 60, + 0, + 0, + 0}); + this.numFPS.Minimum = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.numFPS.Name = "numFPS"; + this.numFPS.Size = new System.Drawing.Size(59, 22); + this.numFPS.TabIndex = 5; + this.numFPS.Value = new decimal(new int[] { + 20, + 0, + 0, + 0}); + // // picPreview // - this.picPreview.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) + this.picPreview.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.picPreview.BackColor = System.Drawing.Color.Black; this.picPreview.ImageShow = null; this.picPreview.Location = new System.Drawing.Point(12, 12); this.picPreview.Name = "picPreview"; - this.picPreview.Size = new System.Drawing.Size(294, 356); + this.picPreview.Size = new System.Drawing.Size(217, 268); this.picPreview.TabIndex = 1; this.picPreview.TabStop = false; // // ctrOutput // - this.ctrOutput.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) + this.ctrOutput.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ctrOutput.Location = new System.Drawing.Point(12, 79); this.ctrOutput.Name = "ctrOutput"; - this.ctrOutput.Size = new System.Drawing.Size(294, 349); + this.ctrOutput.Size = new System.Drawing.Size(217, 437); this.ctrOutput.TabIndex = 2; this.ctrOutput.Text = "ctrOutput1"; // @@ -167,6 +193,7 @@ this.splitOutput.Panel2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.splitOutput)).EndInit(); this.splitOutput.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.numFPS)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.picPreview)).EndInit(); this.ResumeLayout(false); @@ -181,6 +208,7 @@ private System.Windows.Forms.SplitContainer splitOutput; private System.Windows.Forms.Button btnStartEnd; private System.Windows.Forms.ComboBox ddlAutomationBot; + private System.Windows.Forms.NumericUpDown numFPS; } } diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.cs b/VAR.ScreenAutomation/FrmScreenAutomation.cs index 400b1ab..2697599 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.cs +++ b/VAR.ScreenAutomation/FrmScreenAutomation.cs @@ -40,7 +40,7 @@ namespace VAR.ScreenAutomation if (components == null) { components = new Container(); } timTicker = new Timer(components) { - Interval = 100, + Interval = Convert.ToInt32(1000 / numFPS.Value), }; timTicker.Tick += TimTicker_Tick; timTicker.Enabled = true; @@ -70,6 +70,7 @@ namespace VAR.ScreenAutomation } picPreview.ImageShow = bmpScreen; + timTicker.Interval = Convert.ToInt32(1000 / numFPS.Value); timTicker.Enabled = true; timTicker.Start(); } From 923d889a8fb61f8474473789e82075875bdffa39 Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Sat, 2 Nov 2019 22:34:07 +0100 Subject: [PATCH 07/26] TetrisBot --- VAR.ScreenAutomation/Bots/TetrisBot.cs | 761 ++++++++++++++++++ .../VAR.ScreenAutomation.csproj | 1 + 2 files changed, 762 insertions(+) create mode 100644 VAR.ScreenAutomation/Bots/TetrisBot.cs diff --git a/VAR.ScreenAutomation/Bots/TetrisBot.cs b/VAR.ScreenAutomation/Bots/TetrisBot.cs new file mode 100644 index 0000000..c61cbde --- /dev/null +++ b/VAR.ScreenAutomation/Bots/TetrisBot.cs @@ -0,0 +1,761 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using VAR.ScreenAutomation.Interfaces; + +namespace VAR.ScreenAutomation.Bots +{ + public class TetrisBot : IAutomationBot + { + private TetrisGrid _grid = new TetrisGrid(); + + private List _currentShape = new List(); + private TetrisGrid _workGrid = new TetrisGrid(); + + private bool _shapeFound = false; + private int _shapeX; + private int _shapeY; + private double _bestEvaluation = double.MinValue; + private int _bestXOffset = 0; + private int _bestRotation = 0; + + public string Name => "Tetris"; + + public void Init(IOutputHandler output) + { + _currentShape.Add(new TetrisShape()); + _currentShape.Add(new TetrisShape()); + _currentShape.Add(new TetrisShape()); + _currentShape.Add(new TetrisShape()); + output.Clean(); + } + + public Bitmap Process(Bitmap bmpInput, IOutputHandler output) + { + // Initialize grid + _grid.SampleFromBitmap(bmpInput); + _grid.MarkGround(); + + // Identify current tetronino + _shapeFound = false; + if (_grid.SearchFirstCell(1, out _shapeX, out _shapeY)) + { + _currentShape[0].SampleFromGrid(_grid, _shapeX, _shapeY, 1); + _shapeFound = _currentShape[0].IsValid(); + for (int i = 1; i < 4; i++) + { + _currentShape[i].RotateCW(_currentShape[i - 1]); + } + } + + // Search best action + _bestEvaluation = double.MinValue; + _bestXOffset = 0; + _bestRotation = 0; + if (_shapeFound) + { + _workGrid.SampleOther(_grid, 2, 2); + if (_currentShape[0].Drop(_workGrid, _shapeX, _shapeY, 1)) + { + _bestXOffset = 0; + _bestRotation = 0; + _bestEvaluation = EvaluateWorkingGrid(); + } + + int offsetX = 1; + double newEvaluation; + + for (int rotation = 0; rotation < 4; rotation++) + { + // Check positive offset + offsetX = 1; + do + { + _workGrid.SampleOther(_grid, 2, 2); + if (_currentShape[rotation].Drop(_workGrid, _shapeX + offsetX, _shapeY, 1)) + { + newEvaluation = EvaluateWorkingGrid(); + if (newEvaluation > _bestEvaluation) + { + _bestEvaluation = newEvaluation; + _bestXOffset = offsetX; + _bestRotation = rotation; + } + } + else + { + break; + } + offsetX++; + } while (true); + + // Check negative offset + offsetX = -1; + do + { + _workGrid.SampleOther(_grid, 2, 2); + if (_currentShape[rotation].Drop(_workGrid, _shapeX + offsetX, _shapeY, 1)) + { + newEvaluation = EvaluateWorkingGrid(); + if (newEvaluation > _bestEvaluation) + { + _bestEvaluation = newEvaluation; + _bestXOffset = offsetX; + _bestRotation = rotation; + } + } + else + { + break; + } + offsetX--; + } while (true); + } + } + else + { + _workGrid.SampleOther(_grid, 2, 2); + } + + // DEBUG Show information + _workGrid.SampleOther(_grid, 2, 2); + if (_shapeFound) + { + _currentShape[_bestRotation].Drop(_workGrid, _shapeX + _bestXOffset, _shapeY, 1); + } + _workGrid.Draw(bmpInput); + + return bmpInput; + } + + private double EvaluateWorkingGrid() + { + //return _workGrid.Evaluate( + // aggregateHeightWeight: -0.510066, + // completeLinesWeight: 0.760666, + // holesWeight: -0.35663, + // bumpinessWeight: -0.184483, + // maxHeightWeight: 0); + return _workGrid.Evaluate( + aggregateHeightWeight: -0.6, + completeLinesWeight: 0.8, + holesWeight: -0.5, + bumpinessWeight: -0.2, + maxHeightWeight: -0.25); + } + + private bool _canShot = true; + private int _lastShotShapeY = 0; + private DateTime _lastShotDateTime; + + public string ResponseKeys() + { + if (_shapeFound == false) { return string.Empty; } + + if (_canShot == false && (_shapeY < _lastShotShapeY || _lastShotDateTime.AddMilliseconds(500) < DateTime.UtcNow)) + { + _canShot = true; + } + + if (_bestRotation == 0 && _bestXOffset == 0) + { + if (_canShot) + { + _canShot = false; + _lastShotShapeY = _shapeY; + _lastShotDateTime = DateTime.UtcNow; + return " "; + } + } + + if (_bestRotation != 0) { return "{UP}"; } + if (_bestXOffset < 0) { return "{LEFT}"; } + if (_bestXOffset > 0) { return "{RIGHT}"; } + + return string.Empty; + } + } + + public class TetrisShape + { + public const int ShapeSize = 4; + + private byte[][] _cells = null; + + private int _count = 0; + + public TetrisShape(byte[][] cells = null) + { + _cells = new byte[ShapeSize][]; + for (int y = 0; y < ShapeSize; y++) + { + _cells[y] = new byte[ShapeSize]; + } + + _count = 0; + if (cells != null) + { + for (int j = 0; j < ShapeSize; j++) + { + if (j >= cells.Length) { break; } + for (int i = 0; i < ShapeSize; i++) + { + if (i >= cells[j].Length) { break; } + _cells[j][i] = cells[j][i]; + if (_cells[j][i] != 0) { _count++; } + } + } + } + } + + public int GetCount() + { + return _count; + } + + private static List _defaultShapes = null; + + public bool IsValid() + { + if (_defaultShapes == null) + { + _defaultShapes = new List + { + // I + new TetrisShape(new byte[][]{ + new byte[]{ 1, 1, 1, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, }, + new byte[]{ 1, }, + new byte[]{ 1, }, + new byte[]{ 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, }, + new byte[]{ 1, }, + new byte[]{ 1, }, + new byte[]{ 1, }, + }), + + // J + new TetrisShape(new byte[][]{ + new byte[]{ 1, }, + new byte[]{ 1, 1, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, 1, }, + new byte[]{ 1, }, + new byte[]{ 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, 1, 1, }, + new byte[]{ 0, 0, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 0, 1, }, + new byte[]{ 0, 1, }, + new byte[]{ 1, 1, }, + }), + + // L + new TetrisShape(new byte[][]{ + new byte[]{ 0, 0, 1, }, + new byte[]{ 1, 1, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, }, + new byte[]{ 1, }, + new byte[]{ 1, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, 1, 1, }, + new byte[]{ 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, 1, }, + new byte[]{ 0, 1, }, + new byte[]{ 0, 1, }, + }), + + // S + new TetrisShape(new byte[][]{ + new byte[]{ 0, 1, 1, }, + new byte[]{ 1, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, }, + new byte[]{ 1, 1, }, + new byte[]{ 0, 1, }, + }), + + // T + new TetrisShape(new byte[][]{ + new byte[]{ 0, 1, }, + new byte[]{ 1, 1, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, }, + new byte[]{ 1, 1, }, + new byte[]{ 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, 1, 1, }, + new byte[]{ 0, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 0, 1, }, + new byte[]{ 1, 1, }, + new byte[]{ 0, 1, }, + }), + + // Z + new TetrisShape(new byte[][]{ + new byte[]{ 1, 1, }, + new byte[]{ 0, 1, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 0, 1, }, + new byte[]{ 1, 1, }, + new byte[]{ 1, }, + }), + + // O + new TetrisShape(new byte[][]{ + new byte[]{ 1, 1, }, + new byte[]{ 1, 1, }, + }) + }; + } + + if (_count != 4) { return false; } + bool matchesAnyDefault = _defaultShapes.Any(ts => CompareShape(ts)); + return matchesAnyDefault; + } + + public void Copy(TetrisShape shape) + { + for (int j = 0; j < ShapeSize; j++) + { + for (int i = 0; i < ShapeSize; i++) + { + _cells[j][i] = shape._cells[j][i]; + _count = shape._count; + } + } + } + + public bool CompareShape(TetrisShape shape) + { + for (int j = 0; j < ShapeSize; j++) + { + for (int i = 0; i < ShapeSize; i++) + { + if (_cells[j][i] != shape._cells[j][i]) + { + return false; + } + } + } + return true; + } + + public void SampleFromGrid(TetrisGrid grid, int x, int y, byte value) + { + _count = 0; + for (int j = 0; j < ShapeSize; j++) + { + for (int i = 0; i < ShapeSize; i++) + { + if (grid.Get(x + i, y + j) == value) + { + _cells[j][i] = 1; + _count++; + } + else + { + _cells[j][i] = 0; + } + } + } + } + + public void PutOnGrid(TetrisGrid grid, int x, int y, byte value) + { + for (int j = 0; j < ShapeSize; j++) + { + for (int i = 0; i < ShapeSize; i++) + { + if (_cells[j][i] == 0) { continue; } + grid.Set(x + i, y + j, value); + } + } + } + + public bool CheckIntersection(TetrisGrid grid, int x, int y) + { + for (int j = 0; j < ShapeSize; j++) + { + for (int i = 0; i < ShapeSize; i++) + { + if (_cells[j][i] == 0) { continue; } + if (grid.Get(x + i, y + j) != 0) + { + return true; + } + } + } + return false; + } + + public bool Drop(TetrisGrid grid, int x, int y, byte value) + { + if (CheckIntersection(grid, x, y)) { return false; } + while (CheckIntersection(grid, x, y + 1) == false) + { + y++; + } + PutOnGrid(grid, x, y, value); + return true; + } + + public bool SearchFirstCell(byte value, out int x, out int y) + { + x = -1; + y = -1; + for (int j = 0; j < ShapeSize && y == -1; j++) + { + for (int i = 0; i < ShapeSize && y == -1; i++) + { + if (_cells[j][i] == value) + { + y = j; + } + } + } + if (y == -1) { return false; } + for (int i = 0; i < ShapeSize && x == -1; i++) + { + for (int j = 0; j < ShapeSize && x == -1; j++) + { + if (_cells[j][i] == value) + { + x = i; + } + } + } + if (x == -1) { return false; } + return true; + } + + public void Offset(int x, int y) + { + _count = 0; + for (int j = 0; j < ShapeSize; j++) + { + for (int i = 0; i < ShapeSize; i++) + { + if ((j + y) < ShapeSize && (i + x) < ShapeSize) + { + _cells[j][i] = _cells[j + y][i + x]; + if (_cells[j][i] != 0) + { + _count++; + } + } + else + { + _cells[j][i] = 0; + } + } + } + } + + public void RotateCW(TetrisShape shape) + { + for (int j = 0; j < ShapeSize; j++) + { + for (int i = 0; i < ShapeSize; i++) + { + _cells[i][ShapeSize - (j + 1)] = shape._cells[j][i]; + } + } + _count = shape._count; + + if (SearchFirstCell(1, out int offsetX, out int offsetY)) + { + Offset(offsetX, offsetY); + } + } + + public void Print(IOutputHandler output) + { + for (int y = 0; y < ShapeSize; y++) + { + StringBuilder sbLine = new StringBuilder(); + for (int x = 0; x < ShapeSize; x++) + { + if (_cells[y][x] == 0) + { + sbLine.Append(".."); + } + else + { + sbLine.Append("[]"); + } + } + output.AddLine(sbLine.ToString()); + } + } + } + + public class TetrisGrid + { + public const int GridWidth = 10; + public const int GridHeight = 24; + + private byte[][] _grid = null; + + private int[] _heights = null; + + public TetrisGrid() + { + _grid = new byte[GridHeight][]; + for (int y = 0; y < GridHeight; y++) + { + _grid[y] = new byte[GridWidth]; + } + _heights = new int[GridWidth]; + } + + public byte Get(int x, int y) + { + if (x >= GridWidth || x < 0) { return 0xFF; } + if (y >= GridHeight || y < 0) { return 0xFF; } + return _grid[y][x]; + } + + public void Set(int x, int y, byte value) + { + if (x >= GridWidth || x < 0) { return; } + if (y >= GridHeight || y < 0) { return; } + _grid[y][x] = value; + } + + public void SampleFromBitmap(Bitmap bmp) + { + float xStep = bmp.Width / GridWidth; + float yStep = bmp.Height / GridHeight; + for (int y = 0; y < GridHeight; y++) + { + for (int x = 0; x < GridWidth; x++) + { + Color color = bmp.GetPixel( + x: (int)((x * xStep) + (xStep / 2)), + y: (int)((y * yStep) + (yStep / 2))); + if (color.R > 128 || color.G > 128 || color.B > 128) + { + _grid[y][x] = 1; + } + else + { + _grid[y][x] = 0; + } + } + } + } + + public void MarkGround() + { + for (int i = 0; i < GridWidth; i++) + { + if (_grid[GridHeight - 1][i] == 1) + { + FloodFill(i, GridHeight - 1, 1, 2); + } + } + } + + public void FloodFill(int x, int y, byte expectedValue, byte fillValue) + { + if (x >= GridWidth || x < 0) { return; } + if (y >= GridHeight || y < 0) { return; } + if (_grid[y][x] != expectedValue) { return; } + _grid[y][x] = fillValue; + FloodFill(x - 1, y - 1, expectedValue, fillValue); + FloodFill(x - 1, y + 0, expectedValue, fillValue); + FloodFill(x - 1, y + 1, expectedValue, fillValue); + FloodFill(x + 0, y - 1, expectedValue, fillValue); + FloodFill(x + 0, y + 1, expectedValue, fillValue); + FloodFill(x + 1, y - 1, expectedValue, fillValue); + FloodFill(x + 1, y + 0, expectedValue, fillValue); + FloodFill(x + 1, y + 1, expectedValue, fillValue); + } + + public void SampleOther(TetrisGrid grid, byte value, byte setValue = 1) + { + for (int y = 0; y < GridHeight; y++) + { + for (int x = 0; x < GridWidth; x++) + { + if (grid._grid[y][x] == value) + { + _grid[y][x] = setValue; + } + else + { + _grid[y][x] = 0; + } + } + } + } + + public bool SearchFirstCell(byte value, out int x, out int y) + { + x = -1; + y = -1; + for (int j = 0; j < GridHeight && y == -1; j++) + { + for (int i = 0; i < GridWidth && y == -1; i++) + { + if (_grid[j][i] == value) + { + y = j; + } + } + } + if (y == -1) { return false; } + for (int i = 0; i < GridWidth && x == -1; i++) + { + for (int j = 0; j < GridHeight && x == -1; j++) + { + if (_grid[j][i] == value) + { + x = i; + } + } + } + if (x == -1) { return false; } + return true; + } + + public double Evaluate(double aggregateHeightWeight, double completeLinesWeight, double holesWeight, double bumpinessWeight, double maxHeightWeight) + { + // Calculte aggregate height + for (int i = 0; i < GridWidth; i++) + { + int j = 0; + while (j < GridHeight && _grid[j][i] == 0) { j++; } + _heights[i] = GridHeight - j; + } + double agregateHeight = _heights.Sum(); + + // Calculate complete lines + int completeLines = 0; + for (int y = 0; y < GridHeight; y++) + { + bool complete = true; + for (int x = 0; x < GridWidth; x++) + { + if (_grid[y][x] == 0) + { + complete = false; + break; + } + } + if (complete) { completeLines++; } + } + + // Calculate holes + int holes = 0; + for (int y = 1; y < GridHeight; y++) + { + for (int x = 0; x < GridWidth; x++) + { + if (_grid[y - 1][x] == 0 && _grid[y][x] != 0) + { + holes++; + } + } + } + + // Calculate bumpiness + int bumpines = 0; + for (int i = 1; i < GridWidth; i++) + { + bumpines += Math.Abs(_heights[i] - _heights[i - 1]); + } + + // Calculate max height + int maxHeight = _heights.Max(); + + // Evaluate formula + double evaluationValue = + aggregateHeightWeight * agregateHeight + + completeLinesWeight * completeLines + + holesWeight * holes + + bumpinessWeight * bumpines + + maxHeightWeight * maxHeight + + 0; + return evaluationValue; + } + + public void Print(IOutputHandler output) + { + for (int y = 0; y < GridHeight; y++) + { + StringBuilder sbLine = new StringBuilder(); + for (int x = 0; x < GridWidth; x++) + { + if (_grid[y][x] == 0) + { + sbLine.Append(".."); + } + else if (_grid[y][x] == 1) + { + sbLine.Append("$$"); + } + else + { + sbLine.Append("[]"); + } + } + output.AddLine(sbLine.ToString()); + } + } + + public void Draw(Bitmap bmp) + { + float xStep = bmp.Width / (float)GridWidth; + float yStep = bmp.Height / (float)GridHeight; + using (Graphics g = Graphics.FromImage(bmp)) + { + g.Clear(Color.Black); + for (int y = 0; y < GridHeight; y++) + { + for (int x = 0; x < GridWidth; x++) + { + Brush br; + if (_grid[y][x] == 0) + { + br = null; + } + else if (_grid[y][x] == 1) + { + br = Brushes.Blue; + } + else + { + br = Brushes.Red; + } + if (br == null) { continue; } + g.FillRectangle(br, xStep * x, yStep * y, xStep, yStep); + } + } + } + } + + } +} diff --git a/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj b/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj index faddfd1..91ef47e 100644 --- a/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj +++ b/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj @@ -50,6 +50,7 @@ + From 9bc7a542b4e975042ee773c5f7174a0e1ef5a2c9 Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Sun, 3 Nov 2019 18:34:36 +0100 Subject: [PATCH 08/26] TetrisBot: Better heuristics. Clearer representation of internal state. --- VAR.ScreenAutomation/Bots/TetrisBot.cs | 95 ++++++++++++++++++-------- 1 file changed, 68 insertions(+), 27 deletions(-) diff --git a/VAR.ScreenAutomation/Bots/TetrisBot.cs b/VAR.ScreenAutomation/Bots/TetrisBot.cs index c61cbde..2fd3421 100644 --- a/VAR.ScreenAutomation/Bots/TetrisBot.cs +++ b/VAR.ScreenAutomation/Bots/TetrisBot.cs @@ -132,12 +132,6 @@ namespace VAR.ScreenAutomation.Bots private double EvaluateWorkingGrid() { - //return _workGrid.Evaluate( - // aggregateHeightWeight: -0.510066, - // completeLinesWeight: 0.760666, - // holesWeight: -0.35663, - // bumpinessWeight: -0.184483, - // maxHeightWeight: 0); return _workGrid.Evaluate( aggregateHeightWeight: -0.6, completeLinesWeight: 0.8, @@ -159,7 +153,7 @@ namespace VAR.ScreenAutomation.Bots _canShot = true; } - if (_bestRotation == 0 && _bestXOffset == 0) + if (_bestRotation == 0 && _bestXOffset == 0 && _bestEvaluation > double.MinValue) { if (_canShot) { @@ -170,6 +164,8 @@ namespace VAR.ScreenAutomation.Bots } } + if (_bestRotation != 0 && _bestXOffset < 0) { return "{UP}{LEFT}"; } + if (_bestRotation != 0 && _bestXOffset > 0) { return "{UP}{RIGHT}"; } if (_bestRotation != 0) { return "{UP}"; } if (_bestXOffset < 0) { return "{LEFT}"; } if (_bestXOffset > 0) { return "{RIGHT}"; } @@ -548,16 +544,46 @@ namespace VAR.ScreenAutomation.Bots { float xStep = bmp.Width / GridWidth; float yStep = bmp.Height / GridHeight; + float xOff0 = xStep / 2; + float xOff1 = xOff0 / 2; + float xOff2 = xOff0 + xOff1; + float yOff0 = yStep / 2; + float yOff1 = yOff0 / 2; + float yOff2 = yOff0 + yOff1; for (int y = 0; y < GridHeight; y++) { for (int x = 0; x < GridWidth; x++) { Color color = bmp.GetPixel( - x: (int)((x * xStep) + (xStep / 2)), - y: (int)((y * yStep) + (yStep / 2))); + x: (int)((x * xStep) + xOff0), + y: (int)((y * yStep) + yOff0)); if (color.R > 128 || color.G > 128 || color.B > 128) { - _grid[y][x] = 1; + Color color0 = bmp.GetPixel( + x: (int)((x * xStep) + xOff1), + y: (int)((y * yStep) + yOff1)); + Color color1 = bmp.GetPixel( + x: (int)((x * xStep) + xOff1), + y: (int)((y * yStep) + yOff2)); + Color color2 = bmp.GetPixel( + x: (int)((x * xStep) + xOff2), + y: (int)((y * yStep) + yOff1)); + Color color3 = bmp.GetPixel( + x: (int)((x * xStep) + xOff2), + y: (int)((y * yStep) + yOff2)); + if ( + (color0.R > 128 || color0.G > 128 || color0.B > 128) && + (color1.R > 128 || color1.G > 128 || color1.B > 128) && + (color2.R > 128 || color2.G > 128 || color2.B > 128) && + (color3.R > 128 || color3.G > 128 || color3.B > 128) && + true) + { + _grid[y][x] = 1; + } + else + { + _grid[y][x] = 0; + } } else { @@ -641,6 +667,20 @@ namespace VAR.ScreenAutomation.Bots return true; } + public bool IsCompleteLine(int y) + { + bool complete = true; + for (int x = 0; x < GridWidth; x++) + { + if (_grid[y][x] == 0) + { + complete = false; + break; + } + } + return complete; + } + public double Evaluate(double aggregateHeightWeight, double completeLinesWeight, double holesWeight, double bumpinessWeight, double maxHeightWeight) { // Calculte aggregate height @@ -656,25 +696,21 @@ namespace VAR.ScreenAutomation.Bots int completeLines = 0; for (int y = 0; y < GridHeight; y++) { - bool complete = true; - for (int x = 0; x < GridWidth; x++) - { - if (_grid[y][x] == 0) - { - complete = false; - break; - } - } - if (complete) { completeLines++; } + if (IsCompleteLine(y)) { completeLines++; } } // Calculate holes int holes = 0; - for (int y = 1; y < GridHeight; y++) + for (int x = 0; x < GridWidth; x++) { - for (int x = 0; x < GridWidth; x++) + bool block = false; + for (int y = 1; y < GridHeight; y++) { - if (_grid[y - 1][x] == 0 && _grid[y][x] != 0) + if (_grid[y][x] != 0 && IsCompleteLine(y) == false) + { + block = true; + } + else if (_grid[y][x] == 0 && block) { holes++; } @@ -730,9 +766,14 @@ namespace VAR.ScreenAutomation.Bots { float xStep = bmp.Width / (float)GridWidth; float yStep = bmp.Height / (float)GridHeight; + float halfXStep = xStep / 2; + float halfYStep = yStep / 2; + float offX = halfXStep / 2; + float offY = halfYStep / 2; + using (Graphics g = Graphics.FromImage(bmp)) { - g.Clear(Color.Black); + //g.Clear(Color.Black); for (int y = 0; y < GridHeight; y++) { for (int x = 0; x < GridWidth; x++) @@ -744,14 +785,14 @@ namespace VAR.ScreenAutomation.Bots } else if (_grid[y][x] == 1) { - br = Brushes.Blue; + br = Brushes.Red; } else { - br = Brushes.Red; + br = Brushes.Blue; } if (br == null) { continue; } - g.FillRectangle(br, xStep * x, yStep * y, xStep, yStep); + g.FillRectangle(br, (xStep * x) + offX, (yStep * y) + offY, halfXStep, halfYStep); } } } From 2fb562de19abcd3cbc7cc24ba4b907e2a1a0a44f Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Tue, 5 Nov 2019 02:20:19 +0100 Subject: [PATCH 09/26] Save configuration between executions. --- VAR.ScreenAutomation/Code/Configuration.cs | 159 ++++++++++++++++++ .../FrmScreenAutomation.Designer.cs | 1 + VAR.ScreenAutomation/FrmScreenAutomation.cs | 31 +++- .../VAR.ScreenAutomation.csproj | 1 + 4 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 VAR.ScreenAutomation/Code/Configuration.cs diff --git a/VAR.ScreenAutomation/Code/Configuration.cs b/VAR.ScreenAutomation/Code/Configuration.cs new file mode 100644 index 0000000..38d3421 --- /dev/null +++ b/VAR.ScreenAutomation/Code/Configuration.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace VAR.ScreenAutomation.Code +{ + public class Configuration + { + private Dictionary _configItems = new Dictionary(); + + private string _name = null; + + public Configuration(string name = null) + { + _name = name; + } + + private static string GetConfigFileName(string name = null) + { + string location = System.Reflection.Assembly.GetEntryAssembly().Location; + string path = Path.GetDirectoryName(location); + string filenameWithoutExtension = Path.GetFileNameWithoutExtension(location); + string configFile; + if (string.IsNullOrEmpty(name)) + { + configFile = string.Format("{0}/{1}.cfg", path, filenameWithoutExtension); + } + else + { + configFile = string.Format("{0}/{1}_{2}.cfg", path, filenameWithoutExtension, name); + } + return configFile; + } + + private static string[] GetConfigurationLines(string name = null) + { + string configFile = GetConfigFileName(name); + string[] config; + if (File.Exists(configFile) == false) + { + config = new string[0]; + } + else + { + config = File.ReadAllLines(configFile); + } + return config; + } + + public void Load() + { + _configItems.Clear(); + string[] configLines = GetConfigurationLines(_name); + foreach (string configLine in configLines) + { + int idxSplit = configLine.IndexOf('|'); + if (idxSplit < 0) { continue; } + string configName = configLine.Substring(0, idxSplit); + string configData = configLine.Substring(idxSplit + 1); + + if (_configItems.ContainsKey(configName)) + { + _configItems[configName] = configData; + } + else + { + _configItems.Add(configName, configData); + } + } + } + + public void Save() + { + StringBuilder sbConfig = new StringBuilder(); + foreach (KeyValuePair pair in _configItems) + { + sbConfig.AppendFormat("{0}|{1}\n", pair.Key, pair.Value); + } + string configFileName = GetConfigFileName(_name); + File.WriteAllText(configFileName, sbConfig.ToString()); + } + + public string Get(string key, string defaultValue) + { + if (_configItems == null) { return defaultValue; } + if (_configItems.ContainsKey(key)) + { + return _configItems[key]; + } + return defaultValue; + } + + public int Get(string key, int defaultValue) + { + if (_configItems == null) { return defaultValue; } + if (_configItems.ContainsKey(key)) + { + if (int.TryParse(_configItems[key], out int value)) + { + return value; + } + return defaultValue; + } + return defaultValue; + } + + public bool Get(string key, bool defaultValue) + { + if (_configItems == null) { return defaultValue; } + if (_configItems.ContainsKey(key)) + { + string value = _configItems[key]; + return (value == "true"); + } + return defaultValue; + } + + public void Set(string key, string value) + { + if (_configItems == null) { return; } + if (_configItems.ContainsKey(key)) + { + _configItems[key] = value; + } + else + { + _configItems.Add(key, value); + } + } + + public void Set(string key, int value) + { + if (_configItems == null) { return; } + if (_configItems.ContainsKey(key)) + { + _configItems[key] = Convert.ToString(value); + } + else + { + _configItems.Add(key, Convert.ToString(value)); + } + } + + public void Set(string key, bool value) + { + if (_configItems == null) { return; } + if (_configItems.ContainsKey(key)) + { + _configItems[key] = value ? "true" : "false"; + } + else + { + _configItems.Add(key, value ? "true" : "false"); + } + } + + } +} diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs b/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs index 9b2f5f8..b970596 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs +++ b/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs @@ -183,6 +183,7 @@ this.Margin = new System.Windows.Forms.Padding(4); this.Name = "FrmScreenAutomation"; this.Text = "ScreenAutomation"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmScreenAutomation_FormClosing); this.Load += new System.EventHandler(this.FrmScreenAutomation_Load); ((System.ComponentModel.ISupportInitialize)(this.picCapturer)).EndInit(); this.splitMain.Panel1.ResumeLayout(false); diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.cs b/VAR.ScreenAutomation/FrmScreenAutomation.cs index 2697599..eb2585b 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.cs +++ b/VAR.ScreenAutomation/FrmScreenAutomation.cs @@ -24,18 +24,31 @@ namespace VAR.ScreenAutomation private void FrmScreenAutomation_Load(object sender, EventArgs e) { + var configuration = new Configuration(); + configuration.Load(); + Top = configuration.Get("Top", Top); + Left = configuration.Get("Left", Left); + Width = configuration.Get("Width", Width); + Height = configuration.Get("Height", Height); + splitMain.SplitterDistance = configuration.Get("splitMain.SplitterDistance", splitMain.SplitterDistance); + splitOutput.SplitterDistance = configuration.Get("splitOutput.SplitterDistance", splitOutput.SplitterDistance); + SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.OptimizedDoubleBuffer, true); SetStyle(ControlStyles.SupportsTransparentBackColor, true); TransparencyKey = Color.LimeGreen; picCapturer.BackColor = Color.LimeGreen; - ddlAutomationBot.Items.AddRange(AutomationBotFactory.GetAllAutomationBots()); - ddlAutomationBot.SelectedIndex = 0; + ddlAutomationBot.SelectedItem = configuration.Get("ddlAutomationBot", (string)ddlAutomationBot.SelectedItem); + if (ddlAutomationBot.SelectedIndex < 0) + { + ddlAutomationBot.SelectedIndex = 0; + } _automationBot = AutomationBotFactory.CreateFromName((string)ddlAutomationBot.SelectedItem); _automationBot?.Init(ctrOutput); + numFPS.Value = configuration.Get("numFPS", (int)numFPS.Value); if (components == null) { components = new Container(); } timTicker = new Timer(components) @@ -49,6 +62,20 @@ namespace VAR.ScreenAutomation WindowHandling.WindowSetTopLevel(this); } + private void FrmScreenAutomation_FormClosing(object sender, FormClosingEventArgs e) + { + var configuration = new Configuration(); + configuration.Set("Top", Top); + configuration.Set("Left", Left); + configuration.Set("Width", Width); + configuration.Set("Height", Height); + configuration.Set("splitMain.SplitterDistance", splitMain.SplitterDistance); + configuration.Set("splitOutput.SplitterDistance", splitOutput.SplitterDistance); + configuration.Set("ddlAutomationBot", (string)ddlAutomationBot.SelectedItem); + configuration.Set("numFPS", (int)numFPS.Value); + configuration.Save(); + } + private void TimTicker_Tick(object sender, EventArgs e) { timTicker.Enabled = false; diff --git a/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj b/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj index 91ef47e..36975ae 100644 --- a/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj +++ b/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj @@ -52,6 +52,7 @@ + From 2f8f4492eac65be4ace3604fadcedad3f76497ff Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Tue, 5 Nov 2019 02:36:22 +0100 Subject: [PATCH 10/26] TetrisBot: Better visualization. --- VAR.ScreenAutomation/Bots/TetrisBot.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/VAR.ScreenAutomation/Bots/TetrisBot.cs b/VAR.ScreenAutomation/Bots/TetrisBot.cs index 2fd3421..628fcce 100644 --- a/VAR.ScreenAutomation/Bots/TetrisBot.cs +++ b/VAR.ScreenAutomation/Bots/TetrisBot.cs @@ -771,9 +771,9 @@ namespace VAR.ScreenAutomation.Bots float offX = halfXStep / 2; float offY = halfYStep / 2; + using (Pen borderPen = new Pen(Color.DarkGray)) using (Graphics g = Graphics.FromImage(bmp)) { - //g.Clear(Color.Black); for (int y = 0; y < GridHeight; y++) { for (int x = 0; x < GridWidth; x++) @@ -781,7 +781,7 @@ namespace VAR.ScreenAutomation.Bots Brush br; if (_grid[y][x] == 0) { - br = null; + br = Brushes.Black; } else if (_grid[y][x] == 1) { @@ -792,6 +792,8 @@ namespace VAR.ScreenAutomation.Bots br = Brushes.Blue; } if (br == null) { continue; } + + g.DrawRectangle(borderPen, (xStep * x) + offX - 1, (yStep * y) + offY - 1, halfXStep + 2, halfYStep + 2); g.FillRectangle(br, (xStep * x) + offX, (yStep * y) + offY, halfXStep, halfYStep); } } From 4da5466de957e001c1a94d4824699e26b65340f6 Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Tue, 5 Nov 2019 17:32:14 +0100 Subject: [PATCH 11/26] FrmScreenAutomation: Only top level while running. --- VAR.ScreenAutomation/Code/WindowHandling.cs | 12 ++++++++++-- VAR.ScreenAutomation/FrmScreenAutomation.cs | 3 ++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/VAR.ScreenAutomation/Code/WindowHandling.cs b/VAR.ScreenAutomation/Code/WindowHandling.cs index 33733e5..bf5364a 100644 --- a/VAR.ScreenAutomation/Code/WindowHandling.cs +++ b/VAR.ScreenAutomation/Code/WindowHandling.cs @@ -8,6 +8,7 @@ namespace VAR.ScreenAutomation.Code public class WindowHandling { private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); + private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); private const UInt32 SWP_NOSIZE = 0x0001; private const UInt32 SWP_NOMOVE = 0x0002; private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE; @@ -16,9 +17,16 @@ namespace VAR.ScreenAutomation.Code [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); - public static void WindowSetTopLevel(Form form) + public static void WindowSetTopLevel(Form form, bool top = true) { - SetWindowPos(form.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS); + if (top) + { + SetWindowPos(form.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS); + } + else + { + SetWindowPos(form.Handle, HWND_NOTOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS); + } } public static bool ApplicationIsActivated() diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.cs b/VAR.ScreenAutomation/FrmScreenAutomation.cs index eb2585b..9a78b5a 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.cs +++ b/VAR.ScreenAutomation/FrmScreenAutomation.cs @@ -59,7 +59,6 @@ namespace VAR.ScreenAutomation timTicker.Enabled = true; timTicker.Start(); - WindowHandling.WindowSetTopLevel(this); } private void FrmScreenAutomation_FormClosing(object sender, FormClosingEventArgs e) @@ -118,6 +117,7 @@ namespace VAR.ScreenAutomation { if (_running) { return; } _running = true; + WindowHandling.WindowSetTopLevel(this); btnStartEnd.Text = "End"; _automationBot = AutomationBotFactory.CreateFromName((string)ddlAutomationBot.SelectedItem); _automationBot?.Init(ctrOutput); @@ -131,6 +131,7 @@ namespace VAR.ScreenAutomation if (_running == false) { return; } _running = false; btnStartEnd.Text = "Start"; + WindowHandling.WindowSetTopLevel(this, false); } private void DdlAutomationBot_SelectedIndexChanged(object sender, EventArgs e) From 474757af5eb746afa531360cbd4fcb15e84efcad Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Tue, 5 Nov 2019 17:33:02 +0100 Subject: [PATCH 12/26] Screenshoter: Avoid exception while copy screen when there is no desktop. --- VAR.ScreenAutomation/Code/Screenshoter.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/VAR.ScreenAutomation/Code/Screenshoter.cs b/VAR.ScreenAutomation/Code/Screenshoter.cs index 24b3b35..127e4ed 100644 --- a/VAR.ScreenAutomation/Code/Screenshoter.cs +++ b/VAR.ScreenAutomation/Code/Screenshoter.cs @@ -1,4 +1,5 @@ -using System.Drawing; +using System; +using System.Drawing; using System.Windows.Forms; namespace VAR.ScreenAutomation.Code @@ -29,11 +30,15 @@ namespace VAR.ScreenAutomation.Code bmp = new Bitmap(width ?? 0, height ?? 0); } - // Draw the screenshot into our bitmap. - using (Graphics g = Graphics.FromImage(bmp)) + try { - g.CopyFromScreen(left ?? 0, top ?? 0, 0, 0, bmp.Size); + // Draw the screenshot into our bitmap. + using (Graphics g = Graphics.FromImage(bmp)) + { + g.CopyFromScreen(left ?? 0, top ?? 0, 0, 0, bmp.Size); + } } + catch (Exception) { /* Nom Nom Nom */} return bmp; } } From 0457466bd066e5be1c067daad5236d2da1460b13 Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Tue, 5 Nov 2019 17:34:50 +0100 Subject: [PATCH 13/26] Allow configuration on AutomationBots. --- VAR.ScreenAutomation/Bots/DummyBot.cs | 7 +- VAR.ScreenAutomation/Bots/TetrisBot.cs | 118 ++++++++++------- ...guration.cs => FileBackedConfiguration.cs} | 100 +++++---------- .../Code/MemoryBackedConfiguration.cs | 98 ++++++++++++++ .../FrmAutomationBotParams.Designer.cs | 48 +++++++ .../FrmAutomationBotParams.cs | 79 ++++++++++++ .../FrmAutomationBotParams.resx | 120 ++++++++++++++++++ .../FrmScreenAutomation.Designer.cs | 106 +++++++++------- VAR.ScreenAutomation/FrmScreenAutomation.cs | 37 ++++-- .../Interfaces/IAutomationBot.cs | 3 +- .../Interfaces/IConfiguration.cs | 16 +++ .../VAR.ScreenAutomation.csproj | 13 +- 12 files changed, 575 insertions(+), 170 deletions(-) rename VAR.ScreenAutomation/Code/{Configuration.cs => FileBackedConfiguration.cs} (51%) create mode 100644 VAR.ScreenAutomation/Code/MemoryBackedConfiguration.cs create mode 100644 VAR.ScreenAutomation/FrmAutomationBotParams.Designer.cs create mode 100644 VAR.ScreenAutomation/FrmAutomationBotParams.cs create mode 100644 VAR.ScreenAutomation/FrmAutomationBotParams.resx create mode 100644 VAR.ScreenAutomation/Interfaces/IConfiguration.cs diff --git a/VAR.ScreenAutomation/Bots/DummyBot.cs b/VAR.ScreenAutomation/Bots/DummyBot.cs index 5e43325..7f263f5 100644 --- a/VAR.ScreenAutomation/Bots/DummyBot.cs +++ b/VAR.ScreenAutomation/Bots/DummyBot.cs @@ -7,7 +7,12 @@ namespace VAR.ScreenAutomation.Bots { public string Name => "Dummy"; - public void Init(IOutputHandler output) + public IConfiguration GetDefaultConfiguration() + { + return null; + } + + public void Init(IOutputHandler output, IConfiguration config) { output.Clean(); } diff --git a/VAR.ScreenAutomation/Bots/TetrisBot.cs b/VAR.ScreenAutomation/Bots/TetrisBot.cs index 628fcce..8a94944 100644 --- a/VAR.ScreenAutomation/Bots/TetrisBot.cs +++ b/VAR.ScreenAutomation/Bots/TetrisBot.cs @@ -3,16 +3,17 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; +using VAR.ScreenAutomation.Code; using VAR.ScreenAutomation.Interfaces; namespace VAR.ScreenAutomation.Bots { public class TetrisBot : IAutomationBot { - private TetrisGrid _grid = new TetrisGrid(); + private TetrisGrid _grid; - private List _currentShape = new List(); - private TetrisGrid _workGrid = new TetrisGrid(); + private List _currentShape; + private TetrisGrid _workGrid; private bool _shapeFound = false; private int _shapeX; @@ -23,12 +24,31 @@ namespace VAR.ScreenAutomation.Bots public string Name => "Tetris"; - public void Init(IOutputHandler output) + private const int DefaultGridWidth = 10; + private const int DefaultGridHeight = 20; + + public IConfiguration GetDefaultConfiguration() { - _currentShape.Add(new TetrisShape()); - _currentShape.Add(new TetrisShape()); - _currentShape.Add(new TetrisShape()); - _currentShape.Add(new TetrisShape()); + var defaultConfiguration = new MemoryBackedConfiguration(); + defaultConfiguration.Set("GridWidth", DefaultGridWidth); + defaultConfiguration.Set("GridHeight", DefaultGridHeight); + return defaultConfiguration; + } + + public void Init(IOutputHandler output, IConfiguration config) + { + int gridWidth = config.Get("GridWidth", DefaultGridWidth); + int gridHeight = config.Get("GridHeight", DefaultGridHeight); + + _grid = new TetrisGrid(gridWidth, gridHeight); + _workGrid = new TetrisGrid(gridWidth, gridHeight); + _currentShape = new List + { + new TetrisShape(), + new TetrisShape(), + new TetrisShape(), + new TetrisShape(), + }; output.Clean(); } @@ -509,50 +529,52 @@ namespace VAR.ScreenAutomation.Bots public class TetrisGrid { - public const int GridWidth = 10; - public const int GridHeight = 24; + private int _gridWidth; + private int _gridHeight; private byte[][] _grid = null; private int[] _heights = null; - public TetrisGrid() + public TetrisGrid(int gridWidth, int gridHeight) { - _grid = new byte[GridHeight][]; - for (int y = 0; y < GridHeight; y++) + _gridWidth = gridWidth; + _gridHeight = gridHeight; + _grid = new byte[_gridHeight][]; + for (int y = 0; y < _gridHeight; y++) { - _grid[y] = new byte[GridWidth]; + _grid[y] = new byte[_gridWidth]; } - _heights = new int[GridWidth]; + _heights = new int[_gridWidth]; } public byte Get(int x, int y) { - if (x >= GridWidth || x < 0) { return 0xFF; } - if (y >= GridHeight || y < 0) { return 0xFF; } + if (x >= _gridWidth || x < 0) { return 0xFF; } + if (y >= _gridHeight || y < 0) { return 0xFF; } return _grid[y][x]; } public void Set(int x, int y, byte value) { - if (x >= GridWidth || x < 0) { return; } - if (y >= GridHeight || y < 0) { return; } + if (x >= _gridWidth || x < 0) { return; } + if (y >= _gridHeight || y < 0) { return; } _grid[y][x] = value; } public void SampleFromBitmap(Bitmap bmp) { - float xStep = bmp.Width / GridWidth; - float yStep = bmp.Height / GridHeight; + float xStep = bmp.Width / _gridWidth; + float yStep = bmp.Height / _gridHeight; float xOff0 = xStep / 2; float xOff1 = xOff0 / 2; float xOff2 = xOff0 + xOff1; float yOff0 = yStep / 2; float yOff1 = yOff0 / 2; float yOff2 = yOff0 + yOff1; - for (int y = 0; y < GridHeight; y++) + for (int y = 0; y < _gridHeight; y++) { - for (int x = 0; x < GridWidth; x++) + for (int x = 0; x < _gridWidth; x++) { Color color = bmp.GetPixel( x: (int)((x * xStep) + xOff0), @@ -595,19 +617,19 @@ namespace VAR.ScreenAutomation.Bots public void MarkGround() { - for (int i = 0; i < GridWidth; i++) + for (int i = 0; i < _gridWidth; i++) { - if (_grid[GridHeight - 1][i] == 1) + if (_grid[_gridHeight - 1][i] == 1) { - FloodFill(i, GridHeight - 1, 1, 2); + FloodFill(i, _gridHeight - 1, 1, 2); } } } public void FloodFill(int x, int y, byte expectedValue, byte fillValue) { - if (x >= GridWidth || x < 0) { return; } - if (y >= GridHeight || y < 0) { return; } + if (x >= _gridWidth || x < 0) { return; } + if (y >= _gridHeight || y < 0) { return; } if (_grid[y][x] != expectedValue) { return; } _grid[y][x] = fillValue; FloodFill(x - 1, y - 1, expectedValue, fillValue); @@ -622,9 +644,9 @@ namespace VAR.ScreenAutomation.Bots public void SampleOther(TetrisGrid grid, byte value, byte setValue = 1) { - for (int y = 0; y < GridHeight; y++) + for (int y = 0; y < _gridHeight; y++) { - for (int x = 0; x < GridWidth; x++) + for (int x = 0; x < _gridWidth; x++) { if (grid._grid[y][x] == value) { @@ -642,9 +664,9 @@ namespace VAR.ScreenAutomation.Bots { x = -1; y = -1; - for (int j = 0; j < GridHeight && y == -1; j++) + for (int j = 0; j < _gridHeight && y == -1; j++) { - for (int i = 0; i < GridWidth && y == -1; i++) + for (int i = 0; i < _gridWidth && y == -1; i++) { if (_grid[j][i] == value) { @@ -653,9 +675,9 @@ namespace VAR.ScreenAutomation.Bots } } if (y == -1) { return false; } - for (int i = 0; i < GridWidth && x == -1; i++) + for (int i = 0; i < _gridWidth && x == -1; i++) { - for (int j = 0; j < GridHeight && x == -1; j++) + for (int j = 0; j < _gridHeight && x == -1; j++) { if (_grid[j][i] == value) { @@ -670,7 +692,7 @@ namespace VAR.ScreenAutomation.Bots public bool IsCompleteLine(int y) { bool complete = true; - for (int x = 0; x < GridWidth; x++) + for (int x = 0; x < _gridWidth; x++) { if (_grid[y][x] == 0) { @@ -684,27 +706,27 @@ namespace VAR.ScreenAutomation.Bots public double Evaluate(double aggregateHeightWeight, double completeLinesWeight, double holesWeight, double bumpinessWeight, double maxHeightWeight) { // Calculte aggregate height - for (int i = 0; i < GridWidth; i++) + for (int i = 0; i < _gridWidth; i++) { int j = 0; - while (j < GridHeight && _grid[j][i] == 0) { j++; } - _heights[i] = GridHeight - j; + while (j < _gridHeight && _grid[j][i] == 0) { j++; } + _heights[i] = _gridHeight - j; } double agregateHeight = _heights.Sum(); // Calculate complete lines int completeLines = 0; - for (int y = 0; y < GridHeight; y++) + for (int y = 0; y < _gridHeight; y++) { if (IsCompleteLine(y)) { completeLines++; } } // Calculate holes int holes = 0; - for (int x = 0; x < GridWidth; x++) + for (int x = 0; x < _gridWidth; x++) { bool block = false; - for (int y = 1; y < GridHeight; y++) + for (int y = 1; y < _gridHeight; y++) { if (_grid[y][x] != 0 && IsCompleteLine(y) == false) { @@ -719,7 +741,7 @@ namespace VAR.ScreenAutomation.Bots // Calculate bumpiness int bumpines = 0; - for (int i = 1; i < GridWidth; i++) + for (int i = 1; i < _gridWidth; i++) { bumpines += Math.Abs(_heights[i] - _heights[i - 1]); } @@ -740,10 +762,10 @@ namespace VAR.ScreenAutomation.Bots public void Print(IOutputHandler output) { - for (int y = 0; y < GridHeight; y++) + for (int y = 0; y < _gridHeight; y++) { StringBuilder sbLine = new StringBuilder(); - for (int x = 0; x < GridWidth; x++) + for (int x = 0; x < _gridWidth; x++) { if (_grid[y][x] == 0) { @@ -764,8 +786,8 @@ namespace VAR.ScreenAutomation.Bots public void Draw(Bitmap bmp) { - float xStep = bmp.Width / (float)GridWidth; - float yStep = bmp.Height / (float)GridHeight; + float xStep = bmp.Width / (float)_gridWidth; + float yStep = bmp.Height / (float)_gridHeight; float halfXStep = xStep / 2; float halfYStep = yStep / 2; float offX = halfXStep / 2; @@ -774,9 +796,9 @@ namespace VAR.ScreenAutomation.Bots using (Pen borderPen = new Pen(Color.DarkGray)) using (Graphics g = Graphics.FromImage(bmp)) { - for (int y = 0; y < GridHeight; y++) + for (int y = 0; y < _gridHeight; y++) { - for (int x = 0; x < GridWidth; x++) + for (int x = 0; x < _gridWidth; x++) { Brush br; if (_grid[y][x] == 0) diff --git a/VAR.ScreenAutomation/Code/Configuration.cs b/VAR.ScreenAutomation/Code/FileBackedConfiguration.cs similarity index 51% rename from VAR.ScreenAutomation/Code/Configuration.cs rename to VAR.ScreenAutomation/Code/FileBackedConfiguration.cs index 38d3421..a845532 100644 --- a/VAR.ScreenAutomation/Code/Configuration.cs +++ b/VAR.ScreenAutomation/Code/FileBackedConfiguration.cs @@ -1,17 +1,17 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Text; +using VAR.ScreenAutomation.Interfaces; namespace VAR.ScreenAutomation.Code { - public class Configuration + public class FileBackedConfiguration : IConfiguration { - private Dictionary _configItems = new Dictionary(); + private MemoryBackedConfiguration _config = new MemoryBackedConfiguration(); private string _name = null; - public Configuration(string name = null) + public FileBackedConfiguration(string name = null) { _name = name; } @@ -48,9 +48,16 @@ namespace VAR.ScreenAutomation.Code return config; } - public void Load() + public void Load(IConfiguration other = null) { - _configItems.Clear(); + _config.Clear(); + if (other != null) + { + foreach (string key in other.GetKeys()) + { + _config.Set(key, other.Get(key, null)); + } + } string[] configLines = GetConfigurationLines(_name); foreach (string configLine in configLines) { @@ -59,100 +66,59 @@ namespace VAR.ScreenAutomation.Code string configName = configLine.Substring(0, idxSplit); string configData = configLine.Substring(idxSplit + 1); - if (_configItems.ContainsKey(configName)) - { - _configItems[configName] = configData; - } - else - { - _configItems.Add(configName, configData); - } + _config.Set(configName, configData); } } public void Save() { StringBuilder sbConfig = new StringBuilder(); - foreach (KeyValuePair pair in _configItems) + foreach (string key in _config.GetKeys()) { - sbConfig.AppendFormat("{0}|{1}\n", pair.Key, pair.Value); + sbConfig.AppendFormat("{0}|{1}\n", key, _config.Get(key, string.Empty)); } string configFileName = GetConfigFileName(_name); File.WriteAllText(configFileName, sbConfig.ToString()); } + public IEnumerable GetKeys() + { + return _config.GetKeys(); + } + + public void Clear() + { + _config.Clear(); + } + public string Get(string key, string defaultValue) { - if (_configItems == null) { return defaultValue; } - if (_configItems.ContainsKey(key)) - { - return _configItems[key]; - } - return defaultValue; + return _config.Get(key, defaultValue); } public int Get(string key, int defaultValue) { - if (_configItems == null) { return defaultValue; } - if (_configItems.ContainsKey(key)) - { - if (int.TryParse(_configItems[key], out int value)) - { - return value; - } - return defaultValue; - } - return defaultValue; + return _config.Get(key, defaultValue); } public bool Get(string key, bool defaultValue) { - if (_configItems == null) { return defaultValue; } - if (_configItems.ContainsKey(key)) - { - string value = _configItems[key]; - return (value == "true"); - } - return defaultValue; + return _config.Get(key, defaultValue); } public void Set(string key, string value) { - if (_configItems == null) { return; } - if (_configItems.ContainsKey(key)) - { - _configItems[key] = value; - } - else - { - _configItems.Add(key, value); - } + _config.Set(key, value); } public void Set(string key, int value) { - if (_configItems == null) { return; } - if (_configItems.ContainsKey(key)) - { - _configItems[key] = Convert.ToString(value); - } - else - { - _configItems.Add(key, Convert.ToString(value)); - } + _config.Set(key, value); } public void Set(string key, bool value) { - if (_configItems == null) { return; } - if (_configItems.ContainsKey(key)) - { - _configItems[key] = value ? "true" : "false"; - } - else - { - _configItems.Add(key, value ? "true" : "false"); - } + _config.Set(key, value); } } diff --git a/VAR.ScreenAutomation/Code/MemoryBackedConfiguration.cs b/VAR.ScreenAutomation/Code/MemoryBackedConfiguration.cs new file mode 100644 index 0000000..140313a --- /dev/null +++ b/VAR.ScreenAutomation/Code/MemoryBackedConfiguration.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using VAR.ScreenAutomation.Interfaces; + +namespace VAR.ScreenAutomation.Code +{ + public class MemoryBackedConfiguration : IConfiguration + { + private Dictionary _configItems = new Dictionary(); + + public IEnumerable GetKeys() + { + if (_configItems == null) { return new List(); } + return _configItems.Select(p => p.Key); + } + + public void Clear() + { + _configItems.Clear(); + } + + public string Get(string key, string defaultValue) + { + if (_configItems == null) { return defaultValue; } + if (_configItems.ContainsKey(key)) + { + return _configItems[key]; + } + return defaultValue; + } + + public int Get(string key, int defaultValue) + { + if (_configItems == null) { return defaultValue; } + if (_configItems.ContainsKey(key)) + { + if (int.TryParse(_configItems[key], out int value)) + { + return value; + } + return defaultValue; + } + return defaultValue; + } + + public bool Get(string key, bool defaultValue) + { + if (_configItems == null) { return defaultValue; } + if (_configItems.ContainsKey(key)) + { + string value = _configItems[key]; + return (value == "true"); + } + return defaultValue; + } + + public void Set(string key, string value) + { + if (_configItems == null) { return; } + if (_configItems.ContainsKey(key)) + { + _configItems[key] = value; + } + else + { + _configItems.Add(key, value); + } + } + + public void Set(string key, int value) + { + if (_configItems == null) { return; } + if (_configItems.ContainsKey(key)) + { + _configItems[key] = Convert.ToString(value); + } + else + { + _configItems.Add(key, Convert.ToString(value)); + } + } + + public void Set(string key, bool value) + { + if (_configItems == null) { return; } + if (_configItems.ContainsKey(key)) + { + _configItems[key] = value ? "true" : "false"; + } + else + { + _configItems.Add(key, value ? "true" : "false"); + } + } + + } +} diff --git a/VAR.ScreenAutomation/FrmAutomationBotParams.Designer.cs b/VAR.ScreenAutomation/FrmAutomationBotParams.Designer.cs new file mode 100644 index 0000000..ca46f89 --- /dev/null +++ b/VAR.ScreenAutomation/FrmAutomationBotParams.Designer.cs @@ -0,0 +1,48 @@ +namespace VAR.ScreenAutomation +{ + partial class FrmAutomationBotParams + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.SuspendLayout(); + // + // FrmAutomationBotParams + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(297, 473); + this.Name = "FrmAutomationBotParams"; + this.Text = "AutomationBotParams"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmAutomationBotParams_FormClosing); + this.Load += new System.EventHandler(this.FrmAutomationBotParams_Load); + this.ResumeLayout(false); + + } + + #endregion + } +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/FrmAutomationBotParams.cs b/VAR.ScreenAutomation/FrmAutomationBotParams.cs new file mode 100644 index 0000000..22e58bb --- /dev/null +++ b/VAR.ScreenAutomation/FrmAutomationBotParams.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Windows.Forms; +using VAR.ScreenAutomation.Code; + +namespace VAR.ScreenAutomation +{ + public partial class FrmAutomationBotParams : Form + { + private FileBackedConfiguration _config = null; + + private BindingList pairs = null; + + private DataGridView dgvParams = null; + + public FrmAutomationBotParams(FileBackedConfiguration config) + { + _config = config; + + InitializeComponent(); + } + + private void FrmAutomationBotParams_Load(object sender, EventArgs e) + { + pairs = new BindingList(); + foreach (string key in _config.GetKeys()) + { + pairs.Add(new Pair { Key = key, Value = _config.Get(key, string.Empty), }); + } + pairs.AddingNew += (s, a) => + { + a.NewObject = new Pair { Parent = pairs }; + }; + dgvParams = new DataGridView(); + dgvParams.Dock = DockStyle.Fill; + dgvParams.DataSource = pairs; + + Controls.Add(dgvParams); + } + + private void FrmAutomationBotParams_FormClosing(object sender, FormClosingEventArgs e) + { + var parms = new Dictionary(); + foreach (Pair pair in pairs) + { + if (string.IsNullOrEmpty(pair.Key)) { continue; } + _config.Set(pair.Key, pair.Value); + } + _config.Save(); + } + + internal class Pair : IDataErrorInfo + { + internal IList Parent { get; set; } + public string Key { get; set; } + public string Value { get; set; } + + string IDataErrorInfo.Error + { + get { return string.Empty; } + } + + string IDataErrorInfo.this[string columnName] + { + get + { + if (columnName == "Key" && Parent != null && Parent.Any( + x => x.Key == this.Key && !ReferenceEquals(x, this))) + { + return "duplicate key"; + } + return string.Empty; + } + } + } + } +} diff --git a/VAR.ScreenAutomation/FrmAutomationBotParams.resx b/VAR.ScreenAutomation/FrmAutomationBotParams.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/VAR.ScreenAutomation/FrmAutomationBotParams.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs b/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs index b970596..9140fe1 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs +++ b/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs @@ -31,9 +31,10 @@ this.picCapturer = new System.Windows.Forms.PictureBox(); this.splitMain = new System.Windows.Forms.SplitContainer(); this.splitOutput = new System.Windows.Forms.SplitContainer(); + this.numFPS = new System.Windows.Forms.NumericUpDown(); this.ddlAutomationBot = new System.Windows.Forms.ComboBox(); this.btnStartEnd = new System.Windows.Forms.Button(); - this.numFPS = new System.Windows.Forms.NumericUpDown(); + this.btnAutomationBotConfig = new System.Windows.Forms.Button(); this.picPreview = new VAR.ScreenAutomation.Controls.CtrImageViewer(); this.ctrOutput = new VAR.ScreenAutomation.Controls.CtrOutput(); ((System.ComponentModel.ISupportInitialize)(this.picCapturer)).BeginInit(); @@ -54,11 +55,10 @@ this.picCapturer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.picCapturer.Location = new System.Drawing.Point(4, 4); - this.picCapturer.Margin = new System.Windows.Forms.Padding(4); + this.picCapturer.Location = new System.Drawing.Point(3, 3); this.picCapturer.Name = "picCapturer"; - this.picCapturer.Padding = new System.Windows.Forms.Padding(10); - this.picCapturer.Size = new System.Drawing.Size(501, 799); + this.picCapturer.Padding = new System.Windows.Forms.Padding(8, 8, 8, 8); + this.picCapturer.Size = new System.Drawing.Size(319, 649); this.picCapturer.TabIndex = 0; this.picCapturer.TabStop = false; // @@ -67,7 +67,7 @@ this.splitMain.Dock = System.Windows.Forms.DockStyle.Fill; this.splitMain.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; this.splitMain.Location = new System.Drawing.Point(0, 0); - this.splitMain.Margin = new System.Windows.Forms.Padding(10); + this.splitMain.Margin = new System.Windows.Forms.Padding(8, 8, 8, 8); this.splitMain.Name = "splitMain"; // // splitMain.Panel1 @@ -77,14 +77,16 @@ // splitMain.Panel2 // this.splitMain.Panel2.Controls.Add(this.picCapturer); - this.splitMain.Size = new System.Drawing.Size(754, 816); + this.splitMain.Size = new System.Drawing.Size(566, 663); this.splitMain.SplitterDistance = 232; + this.splitMain.SplitterWidth = 3; this.splitMain.TabIndex = 3; // // splitOutput // this.splitOutput.Dock = System.Windows.Forms.DockStyle.Fill; this.splitOutput.Location = new System.Drawing.Point(0, 0); + this.splitOutput.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.splitOutput.Name = "splitOutput"; this.splitOutput.Orientation = System.Windows.Forms.Orientation.Horizontal; // @@ -94,43 +96,21 @@ // // splitOutput.Panel2 // + this.splitOutput.Panel2.Controls.Add(this.btnAutomationBotConfig); this.splitOutput.Panel2.Controls.Add(this.numFPS); this.splitOutput.Panel2.Controls.Add(this.ddlAutomationBot); this.splitOutput.Panel2.Controls.Add(this.btnStartEnd); this.splitOutput.Panel2.Controls.Add(this.ctrOutput); - this.splitOutput.Size = new System.Drawing.Size(232, 816); - this.splitOutput.SplitterDistance = 283; + this.splitOutput.Size = new System.Drawing.Size(232, 663); + this.splitOutput.SplitterDistance = 229; + this.splitOutput.SplitterWidth = 3; this.splitOutput.TabIndex = 4; // - // ddlAutomationBot - // - this.ddlAutomationBot.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.ddlAutomationBot.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.ddlAutomationBot.FormattingEnabled = true; - this.ddlAutomationBot.Location = new System.Drawing.Point(13, 4); - this.ddlAutomationBot.Name = "ddlAutomationBot"; - this.ddlAutomationBot.Size = new System.Drawing.Size(216, 24); - this.ddlAutomationBot.TabIndex = 4; - this.ddlAutomationBot.SelectedIndexChanged += new System.EventHandler(this.DdlAutomationBot_SelectedIndexChanged); - // - // btnStartEnd - // - this.btnStartEnd.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.btnStartEnd.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.btnStartEnd.Location = new System.Drawing.Point(15, 34); - this.btnStartEnd.Name = "btnStartEnd"; - this.btnStartEnd.Size = new System.Drawing.Size(149, 39); - this.btnStartEnd.TabIndex = 3; - this.btnStartEnd.Text = "Start"; - this.btnStartEnd.UseVisualStyleBackColor = true; - this.btnStartEnd.Click += new System.EventHandler(this.BtnStartEnd_Click); - // // numFPS // this.numFPS.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.numFPS.Location = new System.Drawing.Point(170, 50); + this.numFPS.Location = new System.Drawing.Point(186, 41); + this.numFPS.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.numFPS.Maximum = new decimal(new int[] { 60, 0, @@ -142,7 +122,7 @@ 0, 0}); this.numFPS.Name = "numFPS"; - this.numFPS.Size = new System.Drawing.Size(59, 22); + this.numFPS.Size = new System.Drawing.Size(44, 20); this.numFPS.TabIndex = 5; this.numFPS.Value = new decimal(new int[] { 20, @@ -150,6 +130,44 @@ 0, 0}); // + // ddlAutomationBot + // + this.ddlAutomationBot.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.ddlAutomationBot.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.ddlAutomationBot.FormattingEnabled = true; + this.ddlAutomationBot.Location = new System.Drawing.Point(10, 3); + this.ddlAutomationBot.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.ddlAutomationBot.Name = "ddlAutomationBot"; + this.ddlAutomationBot.Size = new System.Drawing.Size(171, 21); + this.ddlAutomationBot.TabIndex = 4; + this.ddlAutomationBot.SelectedIndexChanged += new System.EventHandler(this.DdlAutomationBot_SelectedIndexChanged); + // + // btnStartEnd + // + this.btnStartEnd.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.btnStartEnd.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btnStartEnd.Location = new System.Drawing.Point(11, 28); + this.btnStartEnd.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.btnStartEnd.Name = "btnStartEnd"; + this.btnStartEnd.Size = new System.Drawing.Size(170, 32); + this.btnStartEnd.TabIndex = 3; + this.btnStartEnd.Text = "Start"; + this.btnStartEnd.UseVisualStyleBackColor = true; + this.btnStartEnd.Click += new System.EventHandler(this.BtnStartEnd_Click); + // + // btnAutomationBotConfig + // + this.btnAutomationBotConfig.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnAutomationBotConfig.Location = new System.Drawing.Point(187, 3); + this.btnAutomationBotConfig.Name = "btnAutomationBotConfig"; + this.btnAutomationBotConfig.Size = new System.Drawing.Size(45, 23); + this.btnAutomationBotConfig.TabIndex = 6; + this.btnAutomationBotConfig.Text = "Cfg."; + this.btnAutomationBotConfig.UseVisualStyleBackColor = true; + this.btnAutomationBotConfig.Click += new System.EventHandler(this.btnAutomationBotConfig_Click); + // // picPreview // this.picPreview.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) @@ -157,9 +175,10 @@ | System.Windows.Forms.AnchorStyles.Right))); this.picPreview.BackColor = System.Drawing.Color.Black; this.picPreview.ImageShow = null; - this.picPreview.Location = new System.Drawing.Point(12, 12); + this.picPreview.Location = new System.Drawing.Point(9, 10); + this.picPreview.Margin = new System.Windows.Forms.Padding(2); this.picPreview.Name = "picPreview"; - this.picPreview.Size = new System.Drawing.Size(217, 268); + this.picPreview.Size = new System.Drawing.Size(221, 217); this.picPreview.TabIndex = 1; this.picPreview.TabStop = false; // @@ -168,19 +187,19 @@ this.ctrOutput.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.ctrOutput.Location = new System.Drawing.Point(12, 79); + this.ctrOutput.Location = new System.Drawing.Point(9, 64); + this.ctrOutput.Margin = new System.Windows.Forms.Padding(2); this.ctrOutput.Name = "ctrOutput"; - this.ctrOutput.Size = new System.Drawing.Size(217, 437); + this.ctrOutput.Size = new System.Drawing.Size(221, 356); this.ctrOutput.TabIndex = 2; this.ctrOutput.Text = "ctrOutput1"; // // FrmScreenAutomation // - this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(754, 816); + this.ClientSize = new System.Drawing.Size(566, 663); this.Controls.Add(this.splitMain); - this.Margin = new System.Windows.Forms.Padding(4); this.Name = "FrmScreenAutomation"; this.Text = "ScreenAutomation"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmScreenAutomation_FormClosing); @@ -210,6 +229,7 @@ private System.Windows.Forms.Button btnStartEnd; private System.Windows.Forms.ComboBox ddlAutomationBot; private System.Windows.Forms.NumericUpDown numFPS; + private System.Windows.Forms.Button btnAutomationBotConfig; } } diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.cs b/VAR.ScreenAutomation/FrmScreenAutomation.cs index 9a78b5a..d02a71a 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.cs +++ b/VAR.ScreenAutomation/FrmScreenAutomation.cs @@ -24,7 +24,7 @@ namespace VAR.ScreenAutomation private void FrmScreenAutomation_Load(object sender, EventArgs e) { - var configuration = new Configuration(); + var configuration = new FileBackedConfiguration(); configuration.Load(); Top = configuration.Get("Top", Top); Left = configuration.Get("Left", Left); @@ -45,8 +45,7 @@ namespace VAR.ScreenAutomation { ddlAutomationBot.SelectedIndex = 0; } - _automationBot = AutomationBotFactory.CreateFromName((string)ddlAutomationBot.SelectedItem); - _automationBot?.Init(ctrOutput); + InitBot((string)ddlAutomationBot.SelectedItem); numFPS.Value = configuration.Get("numFPS", (int)numFPS.Value); @@ -63,7 +62,7 @@ namespace VAR.ScreenAutomation private void FrmScreenAutomation_FormClosing(object sender, FormClosingEventArgs e) { - var configuration = new Configuration(); + var configuration = new FileBackedConfiguration(); configuration.Set("Top", Top); configuration.Set("Left", Left); configuration.Set("Width", Width); @@ -113,14 +112,32 @@ namespace VAR.ScreenAutomation } } + private void DdlAutomationBot_SelectedIndexChanged(object sender, EventArgs e) + { + InitBot((string)ddlAutomationBot.SelectedItem); + } + + private void btnAutomationBotConfig_Click(object sender, EventArgs e) + { + if (_automationBot == null) { return; } + IConfiguration defaultConfig = _automationBot.GetDefaultConfiguration(); + var config = new FileBackedConfiguration(_automationBot.Name); + config.Load(defaultConfig); + var frmAutomationBotParameters = new FrmAutomationBotParams(config) + { + StartPosition = FormStartPosition.CenterParent + }; + frmAutomationBotParameters.ShowDialog(); + InitBot(_automationBot.Name); + } + private void Start() { if (_running) { return; } _running = true; WindowHandling.WindowSetTopLevel(this); btnStartEnd.Text = "End"; - _automationBot = AutomationBotFactory.CreateFromName((string)ddlAutomationBot.SelectedItem); - _automationBot?.Init(ctrOutput); + InitBot((string)ddlAutomationBot.SelectedItem); Point pointCapturerCenter = picCapturer.PointToScreen(new Point(picCapturer.Width / 2, picCapturer.Height / 2)); Mouse.SetPosition((uint)pointCapturerCenter.X, (uint)pointCapturerCenter.Y); Mouse.Click(Mouse.MouseButtons.Left); @@ -134,10 +151,12 @@ namespace VAR.ScreenAutomation WindowHandling.WindowSetTopLevel(this, false); } - private void DdlAutomationBot_SelectedIndexChanged(object sender, EventArgs e) + private void InitBot(string botName) { - _automationBot = AutomationBotFactory.CreateFromName((string)ddlAutomationBot.SelectedItem); - _automationBot?.Init(ctrOutput); + _automationBot = AutomationBotFactory.CreateFromName(botName); + var botConfiguration = new FileBackedConfiguration(botName); + botConfiguration.Load(); + _automationBot?.Init(ctrOutput, botConfiguration); } } } diff --git a/VAR.ScreenAutomation/Interfaces/IAutomationBot.cs b/VAR.ScreenAutomation/Interfaces/IAutomationBot.cs index 1aea4a6..c546956 100644 --- a/VAR.ScreenAutomation/Interfaces/IAutomationBot.cs +++ b/VAR.ScreenAutomation/Interfaces/IAutomationBot.cs @@ -5,7 +5,8 @@ namespace VAR.ScreenAutomation.Interfaces public interface IAutomationBot { string Name { get; } - void Init(IOutputHandler output); + IConfiguration GetDefaultConfiguration(); + void Init(IOutputHandler output, IConfiguration config); Bitmap Process(Bitmap bmpInput, IOutputHandler output); string ResponseKeys(); } diff --git a/VAR.ScreenAutomation/Interfaces/IConfiguration.cs b/VAR.ScreenAutomation/Interfaces/IConfiguration.cs new file mode 100644 index 0000000..a394f27 --- /dev/null +++ b/VAR.ScreenAutomation/Interfaces/IConfiguration.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace VAR.ScreenAutomation.Interfaces +{ + public interface IConfiguration + { + IEnumerable GetKeys(); + void Clear(); + bool Get(string key, bool defaultValue); + int Get(string key, int defaultValue); + string Get(string key, string defaultValue); + void Set(string key, bool value); + void Set(string key, int value); + void Set(string key, string value); + } +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj b/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj index 36975ae..16cb54a 100644 --- a/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj +++ b/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj @@ -52,7 +52,15 @@ - + + + + Form + + + FrmAutomationBotParams.cs + + @@ -72,6 +80,9 @@ + + FrmAutomationBotParams.cs + FrmScreenAutomation.cs From e43ef3dca5d4231a391376dd33244b9aa233a849 Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Thu, 7 Nov 2019 19:50:45 +0100 Subject: [PATCH 14/26] TetrisBot: Remove extra "I" shape. --- VAR.ScreenAutomation/Bots/TetrisBot.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/VAR.ScreenAutomation/Bots/TetrisBot.cs b/VAR.ScreenAutomation/Bots/TetrisBot.cs index 8a94944..d34cf4e 100644 --- a/VAR.ScreenAutomation/Bots/TetrisBot.cs +++ b/VAR.ScreenAutomation/Bots/TetrisBot.cs @@ -249,12 +249,6 @@ namespace VAR.ScreenAutomation.Bots new byte[]{ 1, }, new byte[]{ 1, }, }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, }, - new byte[]{ 1, }, - new byte[]{ 1, }, - new byte[]{ 1, }, - }), // J new TetrisShape(new byte[][]{ From f32b4e4f03729099bb12ffe12fdd19355e04a2bb Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Fri, 8 Nov 2019 02:05:41 +0100 Subject: [PATCH 15/26] TetriBot: Adjust cell values. --- VAR.ScreenAutomation/Bots/TetrisBot.cs | 69 +++++++++++++++----------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/VAR.ScreenAutomation/Bots/TetrisBot.cs b/VAR.ScreenAutomation/Bots/TetrisBot.cs index d34cf4e..8e5a234 100644 --- a/VAR.ScreenAutomation/Bots/TetrisBot.cs +++ b/VAR.ScreenAutomation/Bots/TetrisBot.cs @@ -50,19 +50,19 @@ namespace VAR.ScreenAutomation.Bots new TetrisShape(), }; output.Clean(); + output.AddLine(string.Format("TetrisBot: Starting {0}", DateTime.UtcNow.ToString("s"))); } public Bitmap Process(Bitmap bmpInput, IOutputHandler output) { - // Initialize grid _grid.SampleFromBitmap(bmpInput); - _grid.MarkGround(); - // Identify current tetronino - _shapeFound = false; - if (_grid.SearchFirstCell(1, out _shapeX, out _shapeY)) + // Search shape + _workGrid.SampleOther(_grid, 1, 1); + _workGrid.MarkGround(); + if (_workGrid.SearchFirstCell(1, out _shapeX, out _shapeY)) { - _currentShape[0].SampleFromGrid(_grid, _shapeX, _shapeY, 1); + _currentShape[0].SampleFromGrid(_workGrid, _shapeX, _shapeY, 1); _shapeFound = _currentShape[0].IsValid(); for (int i = 1; i < 4; i++) { @@ -70,14 +70,31 @@ namespace VAR.ScreenAutomation.Bots } } - // Search best action + SearchBestAction(); + + // DEBUG Show information + _workGrid.SampleOther(_grid, 1, 1); + if (_shapeFound) + { + _currentShape[0].PutOnGrid(_workGrid, _shapeX, _shapeY, 0); + _currentShape[_bestRotation].Drop(_workGrid, _shapeX + _bestXOffset, _shapeY, 2); + } + _workGrid.Draw(bmpInput); + + return bmpInput; + } + + private void SearchBestAction() + { _bestEvaluation = double.MinValue; _bestXOffset = 0; _bestRotation = 0; if (_shapeFound) { - _workGrid.SampleOther(_grid, 2, 2); - if (_currentShape[0].Drop(_workGrid, _shapeX, _shapeY, 1)) + _workGrid.SampleOther(_grid, 1, 1); + _currentShape[0].PutOnGrid(_workGrid, _shapeX, _shapeY, 0); + + if (_currentShape[0].Drop(_workGrid, _shapeX, _shapeY, 2)) { _bestXOffset = 0; _bestRotation = 0; @@ -93,8 +110,10 @@ namespace VAR.ScreenAutomation.Bots offsetX = 1; do { - _workGrid.SampleOther(_grid, 2, 2); - if (_currentShape[rotation].Drop(_workGrid, _shapeX + offsetX, _shapeY, 1)) + _workGrid.SampleOther(_grid, 1, 1); + _currentShape[0].PutOnGrid(_workGrid, _shapeX, _shapeY, 0); + + if (_currentShape[rotation].Drop(_workGrid, _shapeX + offsetX, _shapeY, 2)) { newEvaluation = EvaluateWorkingGrid(); if (newEvaluation > _bestEvaluation) @@ -115,8 +134,10 @@ namespace VAR.ScreenAutomation.Bots offsetX = -1; do { - _workGrid.SampleOther(_grid, 2, 2); - if (_currentShape[rotation].Drop(_workGrid, _shapeX + offsetX, _shapeY, 1)) + _workGrid.SampleOther(_grid, 1, 1); + _currentShape[0].PutOnGrid(_workGrid, _shapeX, _shapeY, 0); + + if (_currentShape[rotation].Drop(_workGrid, _shapeX + offsetX, _shapeY, 2)) { newEvaluation = EvaluateWorkingGrid(); if (newEvaluation > _bestEvaluation) @@ -136,18 +157,8 @@ namespace VAR.ScreenAutomation.Bots } else { - _workGrid.SampleOther(_grid, 2, 2); + _workGrid.SampleOther(_grid, 1, 1); } - - // DEBUG Show information - _workGrid.SampleOther(_grid, 2, 2); - if (_shapeFound) - { - _currentShape[_bestRotation].Drop(_workGrid, _shapeX + _bestXOffset, _shapeY, 1); - } - _workGrid.Draw(bmpInput); - - return bmpInput; } private double EvaluateWorkingGrid() @@ -794,19 +805,19 @@ namespace VAR.ScreenAutomation.Bots { for (int x = 0; x < _gridWidth; x++) { - Brush br; + Brush br = null; if (_grid[y][x] == 0) { br = Brushes.Black; } else if (_grid[y][x] == 1) - { - br = Brushes.Red; - } - else { br = Brushes.Blue; } + else if (_grid[y][x] == 2) + { + br = Brushes.Red; + } if (br == null) { continue; } g.DrawRectangle(borderPen, (xStep * x) + offX - 1, (yStep * y) + offY - 1, halfXStep + 2, halfYStep + 2); From 629ad33767026153fb08dc0806521d908da789a5 Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Fri, 8 Nov 2019 02:06:32 +0100 Subject: [PATCH 16/26] FrmScreenAutomation: Add checks for keeping window on top level and click on start. --- .../FrmAutomationBotParams.cs | 8 +- .../FrmScreenAutomation.Designer.cs | 105 ++++++++++++------ VAR.ScreenAutomation/FrmScreenAutomation.cs | 44 ++++++-- 3 files changed, 109 insertions(+), 48 deletions(-) diff --git a/VAR.ScreenAutomation/FrmAutomationBotParams.cs b/VAR.ScreenAutomation/FrmAutomationBotParams.cs index 22e58bb..2647ed8 100644 --- a/VAR.ScreenAutomation/FrmAutomationBotParams.cs +++ b/VAR.ScreenAutomation/FrmAutomationBotParams.cs @@ -33,9 +33,11 @@ namespace VAR.ScreenAutomation { a.NewObject = new Pair { Parent = pairs }; }; - dgvParams = new DataGridView(); - dgvParams.Dock = DockStyle.Fill; - dgvParams.DataSource = pairs; + dgvParams = new DataGridView + { + Dock = DockStyle.Fill, + DataSource = pairs + }; Controls.Add(dgvParams); } diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs b/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs index 9140fe1..c033591 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs +++ b/VAR.ScreenAutomation/FrmScreenAutomation.Designer.cs @@ -31,12 +31,14 @@ this.picCapturer = new System.Windows.Forms.PictureBox(); this.splitMain = new System.Windows.Forms.SplitContainer(); this.splitOutput = new System.Windows.Forms.SplitContainer(); + this.btnAutomationBotConfig = new System.Windows.Forms.Button(); this.numFPS = new System.Windows.Forms.NumericUpDown(); this.ddlAutomationBot = new System.Windows.Forms.ComboBox(); this.btnStartEnd = new System.Windows.Forms.Button(); - this.btnAutomationBotConfig = new System.Windows.Forms.Button(); + this.chkKeepToplevel = new System.Windows.Forms.CheckBox(); this.picPreview = new VAR.ScreenAutomation.Controls.CtrImageViewer(); this.ctrOutput = new VAR.ScreenAutomation.Controls.CtrOutput(); + this.chkClick = new System.Windows.Forms.CheckBox(); ((System.ComponentModel.ISupportInitialize)(this.picCapturer)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.splitMain)).BeginInit(); this.splitMain.Panel1.SuspendLayout(); @@ -55,10 +57,11 @@ this.picCapturer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.picCapturer.Location = new System.Drawing.Point(3, 3); + this.picCapturer.Location = new System.Drawing.Point(4, 4); + this.picCapturer.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.picCapturer.Name = "picCapturer"; - this.picCapturer.Padding = new System.Windows.Forms.Padding(8, 8, 8, 8); - this.picCapturer.Size = new System.Drawing.Size(319, 649); + this.picCapturer.Padding = new System.Windows.Forms.Padding(11, 10, 11, 10); + this.picCapturer.Size = new System.Drawing.Size(505, 799); this.picCapturer.TabIndex = 0; this.picCapturer.TabStop = false; // @@ -67,7 +70,7 @@ this.splitMain.Dock = System.Windows.Forms.DockStyle.Fill; this.splitMain.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; this.splitMain.Location = new System.Drawing.Point(0, 0); - this.splitMain.Margin = new System.Windows.Forms.Padding(8, 8, 8, 8); + this.splitMain.Margin = new System.Windows.Forms.Padding(11, 10, 11, 10); this.splitMain.Name = "splitMain"; // // splitMain.Panel1 @@ -77,16 +80,15 @@ // splitMain.Panel2 // this.splitMain.Panel2.Controls.Add(this.picCapturer); - this.splitMain.Size = new System.Drawing.Size(566, 663); + this.splitMain.Size = new System.Drawing.Size(755, 816); this.splitMain.SplitterDistance = 232; - this.splitMain.SplitterWidth = 3; this.splitMain.TabIndex = 3; // // splitOutput // this.splitOutput.Dock = System.Windows.Forms.DockStyle.Fill; this.splitOutput.Location = new System.Drawing.Point(0, 0); - this.splitOutput.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.splitOutput.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.splitOutput.Name = "splitOutput"; this.splitOutput.Orientation = System.Windows.Forms.Orientation.Horizontal; // @@ -96,21 +98,34 @@ // // splitOutput.Panel2 // + this.splitOutput.Panel2.Controls.Add(this.chkClick); + this.splitOutput.Panel2.Controls.Add(this.chkKeepToplevel); this.splitOutput.Panel2.Controls.Add(this.btnAutomationBotConfig); this.splitOutput.Panel2.Controls.Add(this.numFPS); this.splitOutput.Panel2.Controls.Add(this.ddlAutomationBot); this.splitOutput.Panel2.Controls.Add(this.btnStartEnd); this.splitOutput.Panel2.Controls.Add(this.ctrOutput); - this.splitOutput.Size = new System.Drawing.Size(232, 663); - this.splitOutput.SplitterDistance = 229; - this.splitOutput.SplitterWidth = 3; + this.splitOutput.Size = new System.Drawing.Size(232, 816); + this.splitOutput.SplitterDistance = 281; this.splitOutput.TabIndex = 4; // + // btnAutomationBotConfig + // + this.btnAutomationBotConfig.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnAutomationBotConfig.Location = new System.Drawing.Point(172, 4); + this.btnAutomationBotConfig.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.btnAutomationBotConfig.Name = "btnAutomationBotConfig"; + this.btnAutomationBotConfig.Size = new System.Drawing.Size(60, 28); + this.btnAutomationBotConfig.TabIndex = 6; + this.btnAutomationBotConfig.Text = "Cfg."; + this.btnAutomationBotConfig.UseVisualStyleBackColor = true; + this.btnAutomationBotConfig.Click += new System.EventHandler(this.BtnAutomationBotConfig_Click); + // // numFPS // this.numFPS.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.numFPS.Location = new System.Drawing.Point(186, 41); - this.numFPS.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.numFPS.Location = new System.Drawing.Point(171, 50); + this.numFPS.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.numFPS.Maximum = new decimal(new int[] { 60, 0, @@ -122,7 +137,7 @@ 0, 0}); this.numFPS.Name = "numFPS"; - this.numFPS.Size = new System.Drawing.Size(44, 20); + this.numFPS.Size = new System.Drawing.Size(59, 22); this.numFPS.TabIndex = 5; this.numFPS.Value = new decimal(new int[] { 20, @@ -136,10 +151,10 @@ | System.Windows.Forms.AnchorStyles.Right))); this.ddlAutomationBot.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.ddlAutomationBot.FormattingEnabled = true; - this.ddlAutomationBot.Location = new System.Drawing.Point(10, 3); - this.ddlAutomationBot.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.ddlAutomationBot.Location = new System.Drawing.Point(13, 4); + this.ddlAutomationBot.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.ddlAutomationBot.Name = "ddlAutomationBot"; - this.ddlAutomationBot.Size = new System.Drawing.Size(171, 21); + this.ddlAutomationBot.Size = new System.Drawing.Size(150, 24); this.ddlAutomationBot.TabIndex = 4; this.ddlAutomationBot.SelectedIndexChanged += new System.EventHandler(this.DdlAutomationBot_SelectedIndexChanged); // @@ -148,25 +163,26 @@ this.btnStartEnd.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.btnStartEnd.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.btnStartEnd.Location = new System.Drawing.Point(11, 28); - this.btnStartEnd.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.btnStartEnd.Location = new System.Drawing.Point(15, 34); + this.btnStartEnd.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.btnStartEnd.Name = "btnStartEnd"; - this.btnStartEnd.Size = new System.Drawing.Size(170, 32); + this.btnStartEnd.Size = new System.Drawing.Size(105, 39); this.btnStartEnd.TabIndex = 3; this.btnStartEnd.Text = "Start"; this.btnStartEnd.UseVisualStyleBackColor = true; this.btnStartEnd.Click += new System.EventHandler(this.BtnStartEnd_Click); // - // btnAutomationBotConfig + // chkKeepToplevel // - this.btnAutomationBotConfig.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.btnAutomationBotConfig.Location = new System.Drawing.Point(187, 3); - this.btnAutomationBotConfig.Name = "btnAutomationBotConfig"; - this.btnAutomationBotConfig.Size = new System.Drawing.Size(45, 23); - this.btnAutomationBotConfig.TabIndex = 6; - this.btnAutomationBotConfig.Text = "Cfg."; - this.btnAutomationBotConfig.UseVisualStyleBackColor = true; - this.btnAutomationBotConfig.Click += new System.EventHandler(this.btnAutomationBotConfig_Click); + this.chkKeepToplevel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.chkKeepToplevel.AutoSize = true; + this.chkKeepToplevel.Location = new System.Drawing.Point(15, 498); + this.chkKeepToplevel.Name = "chkKeepToplevel"; + this.chkKeepToplevel.Size = new System.Drawing.Size(117, 21); + this.chkKeepToplevel.TabIndex = 7; + this.chkKeepToplevel.Text = "KeepToplevel"; + this.chkKeepToplevel.UseVisualStyleBackColor = true; + this.chkKeepToplevel.CheckedChanged += new System.EventHandler(this.chkKeepToplevel_CheckedChanged); // // picPreview // @@ -175,10 +191,10 @@ | System.Windows.Forms.AnchorStyles.Right))); this.picPreview.BackColor = System.Drawing.Color.Black; this.picPreview.ImageShow = null; - this.picPreview.Location = new System.Drawing.Point(9, 10); - this.picPreview.Margin = new System.Windows.Forms.Padding(2); + this.picPreview.Location = new System.Drawing.Point(12, 12); + this.picPreview.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.picPreview.Name = "picPreview"; - this.picPreview.Size = new System.Drawing.Size(221, 217); + this.picPreview.Size = new System.Drawing.Size(218, 266); this.picPreview.TabIndex = 1; this.picPreview.TabStop = false; // @@ -187,19 +203,31 @@ this.ctrOutput.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.ctrOutput.Location = new System.Drawing.Point(9, 64); - this.ctrOutput.Margin = new System.Windows.Forms.Padding(2); + this.ctrOutput.Location = new System.Drawing.Point(12, 79); + this.ctrOutput.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.ctrOutput.Name = "ctrOutput"; - this.ctrOutput.Size = new System.Drawing.Size(221, 356); + this.ctrOutput.Size = new System.Drawing.Size(218, 414); this.ctrOutput.TabIndex = 2; this.ctrOutput.Text = "ctrOutput1"; // + // chkClick + // + this.chkClick.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.chkClick.AutoSize = true; + this.chkClick.Location = new System.Drawing.Point(126, 53); + this.chkClick.Name = "chkClick"; + this.chkClick.Size = new System.Drawing.Size(39, 21); + this.chkClick.TabIndex = 8; + this.chkClick.Text = "C"; + this.chkClick.UseVisualStyleBackColor = true; + // // FrmScreenAutomation // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(566, 663); + this.ClientSize = new System.Drawing.Size(755, 816); this.Controls.Add(this.splitMain); + this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.Name = "FrmScreenAutomation"; this.Text = "ScreenAutomation"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmScreenAutomation_FormClosing); @@ -211,6 +239,7 @@ this.splitMain.ResumeLayout(false); this.splitOutput.Panel1.ResumeLayout(false); this.splitOutput.Panel2.ResumeLayout(false); + this.splitOutput.Panel2.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitOutput)).EndInit(); this.splitOutput.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.numFPS)).EndInit(); @@ -230,6 +259,8 @@ private System.Windows.Forms.ComboBox ddlAutomationBot; private System.Windows.Forms.NumericUpDown numFPS; private System.Windows.Forms.Button btnAutomationBotConfig; + private System.Windows.Forms.CheckBox chkKeepToplevel; + private System.Windows.Forms.CheckBox chkClick; } } diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.cs b/VAR.ScreenAutomation/FrmScreenAutomation.cs index d02a71a..058a2c4 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.cs +++ b/VAR.ScreenAutomation/FrmScreenAutomation.cs @@ -18,7 +18,7 @@ namespace VAR.ScreenAutomation public FrmScreenAutomation() { AutoScaleMode = AutoScaleMode.None; - AutoScaleDimensions = new SizeF(1, 1); + Font = new Font(Font.Name, 8.25f * 96f / CreateGraphics().DpiX, Font.Style, Font.Unit, Font.GdiCharSet, Font.GdiVerticalFont); InitializeComponent(); } @@ -45,10 +45,12 @@ namespace VAR.ScreenAutomation { ddlAutomationBot.SelectedIndex = 0; } - InitBot((string)ddlAutomationBot.SelectedItem); numFPS.Value = configuration.Get("numFPS", (int)numFPS.Value); + chkKeepToplevel.Checked = configuration.Get("chkKeepToplevel", chkKeepToplevel.Checked); + chkClick.Checked = configuration.Get("chkClick", chkClick.Checked); + if (components == null) { components = new Container(); } timTicker = new Timer(components) { @@ -71,6 +73,8 @@ namespace VAR.ScreenAutomation configuration.Set("splitOutput.SplitterDistance", splitOutput.SplitterDistance); configuration.Set("ddlAutomationBot", (string)ddlAutomationBot.SelectedItem); configuration.Set("numFPS", (int)numFPS.Value); + configuration.Set("chkKeepToplevel", chkKeepToplevel.Checked); + configuration.Set("chkClick", chkClick.Checked); configuration.Save(); } @@ -117,7 +121,7 @@ namespace VAR.ScreenAutomation InitBot((string)ddlAutomationBot.SelectedItem); } - private void btnAutomationBotConfig_Click(object sender, EventArgs e) + private void BtnAutomationBotConfig_Click(object sender, EventArgs e) { if (_automationBot == null) { return; } IConfiguration defaultConfig = _automationBot.GetDefaultConfiguration(); @@ -135,12 +139,14 @@ namespace VAR.ScreenAutomation { if (_running) { return; } _running = true; - WindowHandling.WindowSetTopLevel(this); btnStartEnd.Text = "End"; InitBot((string)ddlAutomationBot.SelectedItem); - Point pointCapturerCenter = picCapturer.PointToScreen(new Point(picCapturer.Width / 2, picCapturer.Height / 2)); - Mouse.SetPosition((uint)pointCapturerCenter.X, (uint)pointCapturerCenter.Y); - Mouse.Click(Mouse.MouseButtons.Left); + if (chkClick.Checked) + { + Point pointCapturerCenter = picCapturer.PointToScreen(new Point(picCapturer.Width / 2, picCapturer.Height / 2)); + Mouse.SetPosition((uint)pointCapturerCenter.X, (uint)pointCapturerCenter.Y); + Mouse.Click(Mouse.MouseButtons.Left); + } } private void End() @@ -148,7 +154,6 @@ namespace VAR.ScreenAutomation if (_running == false) { return; } _running = false; btnStartEnd.Text = "Start"; - WindowHandling.WindowSetTopLevel(this, false); } private void InitBot(string botName) @@ -158,5 +163,28 @@ namespace VAR.ScreenAutomation botConfiguration.Load(); _automationBot?.Init(ctrOutput, botConfiguration); } + + private void chkKeepToplevel_CheckedChanged(object sender, EventArgs e) + { + WindowSetTopLevel(chkKeepToplevel.Checked); + } + + private bool _isToplevel = false; + + private void WindowSetTopLevel(bool toplevel) + { + if (toplevel) + { + if (_isToplevel) { return; } + WindowHandling.WindowSetTopLevel(this); + _isToplevel = true; + } + else + { + if (_isToplevel == false) { return; } + WindowHandling.WindowSetTopLevel(this, false); + _isToplevel = false; + } + } } } From 64d7c9531b51e3c198c7ce1c8146e506a0d5d3ad Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Fri, 8 Nov 2019 02:07:33 +0100 Subject: [PATCH 17/26] TetrisBot: Better shot heuristics. --- VAR.ScreenAutomation/Bots/TetrisBot.cs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/VAR.ScreenAutomation/Bots/TetrisBot.cs b/VAR.ScreenAutomation/Bots/TetrisBot.cs index 8e5a234..da94a6d 100644 --- a/VAR.ScreenAutomation/Bots/TetrisBot.cs +++ b/VAR.ScreenAutomation/Bots/TetrisBot.cs @@ -171,30 +171,29 @@ namespace VAR.ScreenAutomation.Bots maxHeightWeight: -0.25); } - private bool _canShot = true; - private int _lastShotShapeY = 0; - private DateTime _lastShotDateTime; + private const int ShowCooldownFrames = 2; + private int _shotCooldown = ShowCooldownFrames; public string ResponseKeys() { if (_shapeFound == false) { return string.Empty; } - if (_canShot == false && (_shapeY < _lastShotShapeY || _lastShotDateTime.AddMilliseconds(500) < DateTime.UtcNow)) - { - _canShot = true; - } - if (_bestRotation == 0 && _bestXOffset == 0 && _bestEvaluation > double.MinValue) { - if (_canShot) + if (_shotCooldown <= 0) { - _canShot = false; - _lastShotShapeY = _shapeY; - _lastShotDateTime = DateTime.UtcNow; + _shotCooldown = ShowCooldownFrames; return " "; } + else + { + _shotCooldown--; + return string.Empty; + } } + _shotCooldown = ShowCooldownFrames; + if (_bestRotation != 0 && _bestXOffset < 0) { return "{UP}{LEFT}"; } if (_bestRotation != 0 && _bestXOffset > 0) { return "{UP}{RIGHT}"; } if (_bestRotation != 0) { return "{UP}"; } From 87c5878ec97cdf3f1999bf83533007ec3e9062d7 Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Fri, 8 Nov 2019 02:09:04 +0100 Subject: [PATCH 18/26] TetrisBot: Remove maxHeightWeigth from evaluation function. --- VAR.ScreenAutomation/Bots/TetrisBot.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/VAR.ScreenAutomation/Bots/TetrisBot.cs b/VAR.ScreenAutomation/Bots/TetrisBot.cs index da94a6d..a9804a5 100644 --- a/VAR.ScreenAutomation/Bots/TetrisBot.cs +++ b/VAR.ScreenAutomation/Bots/TetrisBot.cs @@ -164,11 +164,10 @@ namespace VAR.ScreenAutomation.Bots private double EvaluateWorkingGrid() { return _workGrid.Evaluate( - aggregateHeightWeight: -0.6, - completeLinesWeight: 0.8, - holesWeight: -0.5, - bumpinessWeight: -0.2, - maxHeightWeight: -0.25); + aggregateHeightWeight: -0.510066, + completeLinesWeight: 0.760666, + holesWeight: -0.35663, + bumpinessWeight: -0.184483); } private const int ShowCooldownFrames = 2; @@ -707,7 +706,7 @@ namespace VAR.ScreenAutomation.Bots return complete; } - public double Evaluate(double aggregateHeightWeight, double completeLinesWeight, double holesWeight, double bumpinessWeight, double maxHeightWeight) + public double Evaluate(double aggregateHeightWeight, double completeLinesWeight, double holesWeight, double bumpinessWeight) { // Calculte aggregate height for (int i = 0; i < _gridWidth; i++) @@ -750,16 +749,12 @@ namespace VAR.ScreenAutomation.Bots bumpines += Math.Abs(_heights[i] - _heights[i - 1]); } - // Calculate max height - int maxHeight = _heights.Max(); - // Evaluate formula double evaluationValue = aggregateHeightWeight * agregateHeight + completeLinesWeight * completeLines + holesWeight * holes + bumpinessWeight * bumpines + - maxHeightWeight * maxHeight + 0; return evaluationValue; } From c3c9bb88ab23cf00d4045ac3162a25c63b60ace5 Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Fri, 8 Nov 2019 02:09:44 +0100 Subject: [PATCH 19/26] TetrisBot: Split DefaultShapes from IsValid method. --- VAR.ScreenAutomation/Bots/TetrisBot.cs | 219 +++++++++++++------------ 1 file changed, 113 insertions(+), 106 deletions(-) diff --git a/VAR.ScreenAutomation/Bots/TetrisBot.cs b/VAR.ScreenAutomation/Bots/TetrisBot.cs index a9804a5..a90c5b3 100644 --- a/VAR.ScreenAutomation/Bots/TetrisBot.cs +++ b/VAR.ScreenAutomation/Bots/TetrisBot.cs @@ -242,115 +242,122 @@ namespace VAR.ScreenAutomation.Bots private static List _defaultShapes = null; + public static List DefaultShapes + { + get + { + if (_defaultShapes == null) + { + _defaultShapes = new List + { + // I + new TetrisShape(new byte[][]{ + new byte[]{ 1, 1, 1, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, }, + new byte[]{ 1, }, + new byte[]{ 1, }, + new byte[]{ 1, }, + }), + + // J + new TetrisShape(new byte[][]{ + new byte[]{ 1, }, + new byte[]{ 1, 1, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, 1, }, + new byte[]{ 1, }, + new byte[]{ 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, 1, 1, }, + new byte[]{ 0, 0, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 0, 1, }, + new byte[]{ 0, 1, }, + new byte[]{ 1, 1, }, + }), + + // L + new TetrisShape(new byte[][]{ + new byte[]{ 0, 0, 1, }, + new byte[]{ 1, 1, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, }, + new byte[]{ 1, }, + new byte[]{ 1, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, 1, 1, }, + new byte[]{ 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, 1, }, + new byte[]{ 0, 1, }, + new byte[]{ 0, 1, }, + }), + + // S + new TetrisShape(new byte[][]{ + new byte[]{ 0, 1, 1, }, + new byte[]{ 1, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, }, + new byte[]{ 1, 1, }, + new byte[]{ 0, 1, }, + }), + + // T + new TetrisShape(new byte[][]{ + new byte[]{ 0, 1, }, + new byte[]{ 1, 1, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, }, + new byte[]{ 1, 1, }, + new byte[]{ 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 1, 1, 1, }, + new byte[]{ 0, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 0, 1, }, + new byte[]{ 1, 1, }, + new byte[]{ 0, 1, }, + }), + + // Z + new TetrisShape(new byte[][]{ + new byte[]{ 1, 1, }, + new byte[]{ 0, 1, 1, }, + }), + new TetrisShape(new byte[][]{ + new byte[]{ 0, 1, }, + new byte[]{ 1, 1, }, + new byte[]{ 1, }, + }), + + // O + new TetrisShape(new byte[][]{ + new byte[]{ 1, 1, }, + new byte[]{ 1, 1, }, + }) + }; + } + return _defaultShapes; + } + } + public bool IsValid() { - if (_defaultShapes == null) - { - _defaultShapes = new List - { - // I - new TetrisShape(new byte[][]{ - new byte[]{ 1, 1, 1, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, }, - new byte[]{ 1, }, - new byte[]{ 1, }, - new byte[]{ 1, }, - }), - - // J - new TetrisShape(new byte[][]{ - new byte[]{ 1, }, - new byte[]{ 1, 1, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, 1, }, - new byte[]{ 1, }, - new byte[]{ 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, 1, 1, }, - new byte[]{ 0, 0, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 0, 1, }, - new byte[]{ 0, 1, }, - new byte[]{ 1, 1, }, - }), - - // L - new TetrisShape(new byte[][]{ - new byte[]{ 0, 0, 1, }, - new byte[]{ 1, 1, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, }, - new byte[]{ 1, }, - new byte[]{ 1, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, 1, 1, }, - new byte[]{ 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, 1, }, - new byte[]{ 0, 1, }, - new byte[]{ 0, 1, }, - }), - - // S - new TetrisShape(new byte[][]{ - new byte[]{ 0, 1, 1, }, - new byte[]{ 1, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, }, - new byte[]{ 1, 1, }, - new byte[]{ 0, 1, }, - }), - - // T - new TetrisShape(new byte[][]{ - new byte[]{ 0, 1, }, - new byte[]{ 1, 1, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, }, - new byte[]{ 1, 1, }, - new byte[]{ 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, 1, 1, }, - new byte[]{ 0, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 0, 1, }, - new byte[]{ 1, 1, }, - new byte[]{ 0, 1, }, - }), - - // Z - new TetrisShape(new byte[][]{ - new byte[]{ 1, 1, }, - new byte[]{ 0, 1, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 0, 1, }, - new byte[]{ 1, 1, }, - new byte[]{ 1, }, - }), - - // O - new TetrisShape(new byte[][]{ - new byte[]{ 1, 1, }, - new byte[]{ 1, 1, }, - }) - }; - } - if (_count != 4) { return false; } - bool matchesAnyDefault = _defaultShapes.Any(ts => CompareShape(ts)); + bool matchesAnyDefault = DefaultShapes.Any(ts => CompareShape(ts)); return matchesAnyDefault; } From 7379b523346fd74c80c4dc3ff6f986c122afbc8b Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Fri, 8 Nov 2019 02:10:28 +0100 Subject: [PATCH 20/26] TetrisBot.TetrisGrid: Expose Width and Height. --- VAR.ScreenAutomation/Bots/TetrisBot.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/VAR.ScreenAutomation/Bots/TetrisBot.cs b/VAR.ScreenAutomation/Bots/TetrisBot.cs index a90c5b3..1d22407 100644 --- a/VAR.ScreenAutomation/Bots/TetrisBot.cs +++ b/VAR.ScreenAutomation/Bots/TetrisBot.cs @@ -542,6 +542,9 @@ namespace VAR.ScreenAutomation.Bots private int _gridWidth; private int _gridHeight; + public int Width { get { return _gridWidth; } } + public int Height { get { return _gridHeight; } } + private byte[][] _grid = null; private int[] _heights = null; From 7b97e3249e3a46c57eb5c32bc72d932ee68d6d8f Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Sun, 10 Nov 2019 18:23:03 +0100 Subject: [PATCH 21/26] TetrisBot: Better search shape and search best move algorithm. --- VAR.ScreenAutomation/Bots/TetrisBot.cs | 270 +++++++++++++------------ 1 file changed, 141 insertions(+), 129 deletions(-) diff --git a/VAR.ScreenAutomation/Bots/TetrisBot.cs b/VAR.ScreenAutomation/Bots/TetrisBot.cs index 1d22407..b9c73f1 100644 --- a/VAR.ScreenAutomation/Bots/TetrisBot.cs +++ b/VAR.ScreenAutomation/Bots/TetrisBot.cs @@ -13,7 +13,9 @@ namespace VAR.ScreenAutomation.Bots private TetrisGrid _grid; private List _currentShape; - private TetrisGrid _workGrid; + private TetrisGrid _workGrid0; + private TetrisGrid _workGrid1; + private double[] _columnEvaluation; private bool _shapeFound = false; private int _shapeX; @@ -41,7 +43,9 @@ namespace VAR.ScreenAutomation.Bots int gridHeight = config.Get("GridHeight", DefaultGridHeight); _grid = new TetrisGrid(gridWidth, gridHeight); - _workGrid = new TetrisGrid(gridWidth, gridHeight); + _workGrid0 = new TetrisGrid(gridWidth, gridHeight); + _workGrid1 = new TetrisGrid(gridWidth, gridHeight); + _columnEvaluation = new double[gridWidth]; _currentShape = new List { new TetrisShape(), @@ -56,122 +60,119 @@ namespace VAR.ScreenAutomation.Bots public Bitmap Process(Bitmap bmpInput, IOutputHandler output) { _grid.SampleFromBitmap(bmpInput); - - // Search shape - _workGrid.SampleOther(_grid, 1, 1); - _workGrid.MarkGround(); - if (_workGrid.SearchFirstCell(1, out _shapeX, out _shapeY)) - { - _currentShape[0].SampleFromGrid(_workGrid, _shapeX, _shapeY, 1); - _shapeFound = _currentShape[0].IsValid(); - for (int i = 1; i < 4; i++) - { - _currentShape[i].RotateCW(_currentShape[i - 1]); - } - } - + SearchShape(); SearchBestAction(); - // DEBUG Show information - _workGrid.SampleOther(_grid, 1, 1); + // Show information + _workGrid0.SampleOther(_grid, TetrisGrid.Solid, TetrisGrid.Solid); if (_shapeFound) { - _currentShape[0].PutOnGrid(_workGrid, _shapeX, _shapeY, 0); - _currentShape[_bestRotation].Drop(_workGrid, _shapeX + _bestXOffset, _shapeY, 2); + _currentShape[0].PutOnGrid(_workGrid0, _shapeX, _shapeY, TetrisGrid.Empty); + _currentShape[_bestRotation].Drop(_workGrid0, _shapeX + _bestXOffset, _shapeY, TetrisGrid.ShapeA); + _currentShape[0].PutOnGrid(_workGrid0, _shapeX, _shapeY, TetrisGrid.ShapeB); } - _workGrid.Draw(bmpInput); + _workGrid0.Draw(bmpInput, 0.75f); + _workGrid0.SampleOther(_grid, TetrisGrid.Solid, TetrisGrid.Solid); + _workGrid0.RemoveGround(); + _workGrid0.Draw(bmpInput, 0.25f); return bmpInput; } + private void SearchShape() + { + _workGrid0.SampleOther(_grid, TetrisGrid.Solid, TetrisGrid.Solid); + _workGrid0.RemoveGround(); + _shapeFound = false; + for (int y = 0; y < _grid.Height; y++) + { + for (int x = 0; x < _grid.Width; x++) + { + TetrisShape matchedShape = TetrisShape.DefaultShapes.FirstOrDefault(s => s.MatchOnGrid(_workGrid0, x, y)); + if (matchedShape != null) + { + _workGrid1.SampleOther(_workGrid0, TetrisGrid.Solid, TetrisGrid.Solid); + matchedShape.PutOnGrid(_workGrid1, x, y, TetrisGrid.Empty); + if (matchedShape.CheckIntersection(_workGrid1, x, y + 1)) { continue; } + + // Shape found + _currentShape[0].Copy(matchedShape); + for (int i = 1; i < 4; i++) + { + _currentShape[i].RotateCW(_currentShape[i - 1]); + } + _shapeX = x; + _shapeY = y; + _shapeFound = true; + break; + } + } + if (_shapeFound) { break; } + } + } + private void SearchBestAction() { _bestEvaluation = double.MinValue; _bestXOffset = 0; _bestRotation = 0; - if (_shapeFound) + if (!_shapeFound) { - _workGrid.SampleOther(_grid, 1, 1); - _currentShape[0].PutOnGrid(_workGrid, _shapeX, _shapeY, 0); - - if (_currentShape[0].Drop(_workGrid, _shapeX, _shapeY, 2)) - { - _bestXOffset = 0; - _bestRotation = 0; - _bestEvaluation = EvaluateWorkingGrid(); - } - - int offsetX = 1; - double newEvaluation; - - for (int rotation = 0; rotation < 4; rotation++) - { - // Check positive offset - offsetX = 1; - do - { - _workGrid.SampleOther(_grid, 1, 1); - _currentShape[0].PutOnGrid(_workGrid, _shapeX, _shapeY, 0); - - if (_currentShape[rotation].Drop(_workGrid, _shapeX + offsetX, _shapeY, 2)) - { - newEvaluation = EvaluateWorkingGrid(); - if (newEvaluation > _bestEvaluation) - { - _bestEvaluation = newEvaluation; - _bestXOffset = offsetX; - _bestRotation = rotation; - } - } - else - { - break; - } - offsetX++; - } while (true); - - // Check negative offset - offsetX = -1; - do - { - _workGrid.SampleOther(_grid, 1, 1); - _currentShape[0].PutOnGrid(_workGrid, _shapeX, _shapeY, 0); - - if (_currentShape[rotation].Drop(_workGrid, _shapeX + offsetX, _shapeY, 2)) - { - newEvaluation = EvaluateWorkingGrid(); - if (newEvaluation > _bestEvaluation) - { - _bestEvaluation = newEvaluation; - _bestXOffset = offsetX; - _bestRotation = rotation; - } - } - else - { - break; - } - offsetX--; - } while (true); - } + _workGrid0.SampleOther(_grid, TetrisGrid.Solid, TetrisGrid.Solid); + return; } - else + + + for (int rotation = 0; rotation < 4; rotation++) { - _workGrid.SampleOther(_grid, 1, 1); + for (int x = 0; x < _grid.Width; x++) + { + _workGrid0.SampleOther(_grid, TetrisGrid.Solid, TetrisGrid.Solid); + _currentShape[0].PutOnGrid(_workGrid0, _shapeX, _shapeY, TetrisGrid.Empty); + + if (_currentShape[rotation].Drop(_workGrid0, x, _shapeY, TetrisGrid.ShapeA)) + { + double newEvaluation = EvaluateWorkingGrid(); + _columnEvaluation[x] = newEvaluation; + } + else + { + _columnEvaluation[x] = double.MinValue; + } + } + + // Search valid X range + int minX = _shapeX; + while (minX >= 0 && _columnEvaluation[minX] > double.MinValue) { minX--; } + minX++; + int maxX = _shapeX; + while (maxX < _grid.Width && _columnEvaluation[maxX] > double.MinValue) { maxX++; } + maxX--; + + // Apply best value inside valid X range + for (int x = minX; x <= maxX; x++) + { + if (_columnEvaluation[x] > _bestEvaluation) + { + _bestEvaluation = _columnEvaluation[x]; + _bestXOffset = x - _shapeX; + _bestRotation = rotation; + } + } } } private double EvaluateWorkingGrid() { - return _workGrid.Evaluate( + return _workGrid0.Evaluate( aggregateHeightWeight: -0.510066, completeLinesWeight: 0.760666, holesWeight: -0.35663, bumpinessWeight: -0.184483); } - private const int ShowCooldownFrames = 2; - private int _shotCooldown = ShowCooldownFrames; + private const int ShotCooldownFrames = 2; + private int _shotCooldown = ShotCooldownFrames; public string ResponseKeys() { @@ -181,7 +182,7 @@ namespace VAR.ScreenAutomation.Bots { if (_shotCooldown <= 0) { - _shotCooldown = ShowCooldownFrames; + _shotCooldown = ShotCooldownFrames; return " "; } else @@ -191,7 +192,7 @@ namespace VAR.ScreenAutomation.Bots } } - _shotCooldown = ShowCooldownFrames; + _shotCooldown = ShotCooldownFrames; if (_bestRotation != 0 && _bestXOffset < 0) { return "{UP}{LEFT}"; } if (_bestRotation != 0 && _bestXOffset > 0) { return "{UP}{RIGHT}"; } @@ -535,10 +536,41 @@ namespace VAR.ScreenAutomation.Bots output.AddLine(sbLine.ToString()); } } + + public bool MatchOnGrid(TetrisGrid grid, int x, int y) + { + for (int j = 0; j < ShapeSize; j++) + { + for (int i = 0; i < ShapeSize; i++) + { + int currentCell = grid.Get(x + i, y + j); + if (_cells[j][i] == 0) + { + if (currentCell != 0 && currentCell != 0xFF) + { + return false; + } + } + else + { + if (currentCell == 0 || currentCell == 0xFF) + { + return false; + } + } + } + } + return true; + } } public class TetrisGrid { + public const byte Empty = 0; + public const byte Solid = 1; + public const byte ShapeA = 2; + public const byte ShapeB = 3; + private int _gridWidth; private int _gridHeight; @@ -628,13 +660,13 @@ namespace VAR.ScreenAutomation.Bots } } - public void MarkGround() + public void RemoveGround() { for (int i = 0; i < _gridWidth; i++) { if (_grid[_gridHeight - 1][i] == 1) { - FloodFill(i, _gridHeight - 1, 1, 2); + FloodFill(i, _gridHeight - 1, Solid, Empty); } } } @@ -722,7 +754,7 @@ namespace VAR.ScreenAutomation.Bots for (int i = 0; i < _gridWidth; i++) { int j = 0; - while (j < _gridHeight && _grid[j][i] == 0) { j++; } + while (j < _gridHeight && _grid[j][i] == Empty) { j++; } _heights[i] = _gridHeight - j; } double agregateHeight = _heights.Sum(); @@ -741,11 +773,11 @@ namespace VAR.ScreenAutomation.Bots bool block = false; for (int y = 1; y < _gridHeight; y++) { - if (_grid[y][x] != 0 && IsCompleteLine(y) == false) + if (_grid[y][x] != Empty && IsCompleteLine(y) == false) { block = true; } - else if (_grid[y][x] == 0 && block) + else if (_grid[y][x] == Empty && block) { holes++; } @@ -769,38 +801,14 @@ namespace VAR.ScreenAutomation.Bots return evaluationValue; } - public void Print(IOutputHandler output) - { - for (int y = 0; y < _gridHeight; y++) - { - StringBuilder sbLine = new StringBuilder(); - for (int x = 0; x < _gridWidth; x++) - { - if (_grid[y][x] == 0) - { - sbLine.Append(".."); - } - else if (_grid[y][x] == 1) - { - sbLine.Append("$$"); - } - else - { - sbLine.Append("[]"); - } - } - output.AddLine(sbLine.ToString()); - } - } - - public void Draw(Bitmap bmp) + public void Draw(Bitmap bmp, float dotWith = 0.5f) { float xStep = bmp.Width / (float)_gridWidth; float yStep = bmp.Height / (float)_gridHeight; - float halfXStep = xStep / 2; - float halfYStep = yStep / 2; - float offX = halfXStep / 2; - float offY = halfYStep / 2; + float halfXStep = xStep * dotWith; + float halfYStep = yStep * dotWith; + float offX = (xStep - halfXStep) / 2; + float offY = (yStep - halfYStep) / 2; using (Pen borderPen = new Pen(Color.DarkGray)) using (Graphics g = Graphics.FromImage(bmp)) @@ -810,18 +818,22 @@ namespace VAR.ScreenAutomation.Bots for (int x = 0; x < _gridWidth; x++) { Brush br = null; - if (_grid[y][x] == 0) + if (_grid[y][x] == Empty) { br = Brushes.Black; } - else if (_grid[y][x] == 1) + else if (_grid[y][x] == Solid) { br = Brushes.Blue; } - else if (_grid[y][x] == 2) + else if (_grid[y][x] == ShapeA) { br = Brushes.Red; } + else if (_grid[y][x] == ShapeB) + { + br = Brushes.Green; + } if (br == null) { continue; } g.DrawRectangle(borderPen, (xStep * x) + offX - 1, (yStep * y) + offY - 1, halfXStep + 2, halfYStep + 2); From 0385c04136cee298a313b87f1cf831a035fd74ae Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Sun, 10 Nov 2019 18:32:39 +0100 Subject: [PATCH 22/26] TetrisBot: Rename Cell value constants. --- VAR.ScreenAutomation/Bots/TetrisBot.cs | 48 +++++++++++++------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/VAR.ScreenAutomation/Bots/TetrisBot.cs b/VAR.ScreenAutomation/Bots/TetrisBot.cs index b9c73f1..5788906 100644 --- a/VAR.ScreenAutomation/Bots/TetrisBot.cs +++ b/VAR.ScreenAutomation/Bots/TetrisBot.cs @@ -64,15 +64,15 @@ namespace VAR.ScreenAutomation.Bots SearchBestAction(); // Show information - _workGrid0.SampleOther(_grid, TetrisGrid.Solid, TetrisGrid.Solid); + _workGrid0.SampleOther(_grid, TetrisGrid.CellSolid, TetrisGrid.CellSolid); if (_shapeFound) { - _currentShape[0].PutOnGrid(_workGrid0, _shapeX, _shapeY, TetrisGrid.Empty); - _currentShape[_bestRotation].Drop(_workGrid0, _shapeX + _bestXOffset, _shapeY, TetrisGrid.ShapeA); - _currentShape[0].PutOnGrid(_workGrid0, _shapeX, _shapeY, TetrisGrid.ShapeB); + _currentShape[0].PutOnGrid(_workGrid0, _shapeX, _shapeY, TetrisGrid.CellEmpty); + _currentShape[_bestRotation].Drop(_workGrid0, _shapeX + _bestXOffset, _shapeY, TetrisGrid.CellShapeA); + _currentShape[0].PutOnGrid(_workGrid0, _shapeX, _shapeY, TetrisGrid.CellShapeB); } _workGrid0.Draw(bmpInput, 0.75f); - _workGrid0.SampleOther(_grid, TetrisGrid.Solid, TetrisGrid.Solid); + _workGrid0.SampleOther(_grid, TetrisGrid.CellSolid, TetrisGrid.CellSolid); _workGrid0.RemoveGround(); _workGrid0.Draw(bmpInput, 0.25f); @@ -81,7 +81,7 @@ namespace VAR.ScreenAutomation.Bots private void SearchShape() { - _workGrid0.SampleOther(_grid, TetrisGrid.Solid, TetrisGrid.Solid); + _workGrid0.SampleOther(_grid, TetrisGrid.CellSolid, TetrisGrid.CellSolid); _workGrid0.RemoveGround(); _shapeFound = false; for (int y = 0; y < _grid.Height; y++) @@ -91,8 +91,8 @@ namespace VAR.ScreenAutomation.Bots TetrisShape matchedShape = TetrisShape.DefaultShapes.FirstOrDefault(s => s.MatchOnGrid(_workGrid0, x, y)); if (matchedShape != null) { - _workGrid1.SampleOther(_workGrid0, TetrisGrid.Solid, TetrisGrid.Solid); - matchedShape.PutOnGrid(_workGrid1, x, y, TetrisGrid.Empty); + _workGrid1.SampleOther(_workGrid0, TetrisGrid.CellSolid, TetrisGrid.CellSolid); + matchedShape.PutOnGrid(_workGrid1, x, y, TetrisGrid.CellEmpty); if (matchedShape.CheckIntersection(_workGrid1, x, y + 1)) { continue; } // Shape found @@ -118,7 +118,7 @@ namespace VAR.ScreenAutomation.Bots _bestRotation = 0; if (!_shapeFound) { - _workGrid0.SampleOther(_grid, TetrisGrid.Solid, TetrisGrid.Solid); + _workGrid0.SampleOther(_grid, TetrisGrid.CellSolid, TetrisGrid.CellSolid); return; } @@ -127,10 +127,10 @@ namespace VAR.ScreenAutomation.Bots { for (int x = 0; x < _grid.Width; x++) { - _workGrid0.SampleOther(_grid, TetrisGrid.Solid, TetrisGrid.Solid); - _currentShape[0].PutOnGrid(_workGrid0, _shapeX, _shapeY, TetrisGrid.Empty); + _workGrid0.SampleOther(_grid, TetrisGrid.CellSolid, TetrisGrid.CellSolid); + _currentShape[0].PutOnGrid(_workGrid0, _shapeX, _shapeY, TetrisGrid.CellEmpty); - if (_currentShape[rotation].Drop(_workGrid0, x, _shapeY, TetrisGrid.ShapeA)) + if (_currentShape[rotation].Drop(_workGrid0, x, _shapeY, TetrisGrid.CellShapeA)) { double newEvaluation = EvaluateWorkingGrid(); _columnEvaluation[x] = newEvaluation; @@ -566,10 +566,10 @@ namespace VAR.ScreenAutomation.Bots public class TetrisGrid { - public const byte Empty = 0; - public const byte Solid = 1; - public const byte ShapeA = 2; - public const byte ShapeB = 3; + public const byte CellEmpty = 0; + public const byte CellSolid = 1; + public const byte CellShapeA = 2; + public const byte CellShapeB = 3; private int _gridWidth; private int _gridHeight; @@ -666,7 +666,7 @@ namespace VAR.ScreenAutomation.Bots { if (_grid[_gridHeight - 1][i] == 1) { - FloodFill(i, _gridHeight - 1, Solid, Empty); + FloodFill(i, _gridHeight - 1, CellSolid, CellEmpty); } } } @@ -754,7 +754,7 @@ namespace VAR.ScreenAutomation.Bots for (int i = 0; i < _gridWidth; i++) { int j = 0; - while (j < _gridHeight && _grid[j][i] == Empty) { j++; } + while (j < _gridHeight && _grid[j][i] == CellEmpty) { j++; } _heights[i] = _gridHeight - j; } double agregateHeight = _heights.Sum(); @@ -773,11 +773,11 @@ namespace VAR.ScreenAutomation.Bots bool block = false; for (int y = 1; y < _gridHeight; y++) { - if (_grid[y][x] != Empty && IsCompleteLine(y) == false) + if (_grid[y][x] != CellEmpty && IsCompleteLine(y) == false) { block = true; } - else if (_grid[y][x] == Empty && block) + else if (_grid[y][x] == CellEmpty && block) { holes++; } @@ -818,19 +818,19 @@ namespace VAR.ScreenAutomation.Bots for (int x = 0; x < _gridWidth; x++) { Brush br = null; - if (_grid[y][x] == Empty) + if (_grid[y][x] == CellEmpty) { br = Brushes.Black; } - else if (_grid[y][x] == Solid) + else if (_grid[y][x] == CellSolid) { br = Brushes.Blue; } - else if (_grid[y][x] == ShapeA) + else if (_grid[y][x] == CellShapeA) { br = Brushes.Red; } - else if (_grid[y][x] == ShapeB) + else if (_grid[y][x] == CellShapeB) { br = Brushes.Green; } From 089500fcb40850153a408e6cb01474ded09ee91a Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Sun, 10 Nov 2019 18:33:24 +0100 Subject: [PATCH 23/26] TetrisGrid: Put default weights on Evaluate method parameters. --- VAR.ScreenAutomation/Bots/TetrisBot.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/VAR.ScreenAutomation/Bots/TetrisBot.cs b/VAR.ScreenAutomation/Bots/TetrisBot.cs index 5788906..efa2d83 100644 --- a/VAR.ScreenAutomation/Bots/TetrisBot.cs +++ b/VAR.ScreenAutomation/Bots/TetrisBot.cs @@ -132,7 +132,7 @@ namespace VAR.ScreenAutomation.Bots if (_currentShape[rotation].Drop(_workGrid0, x, _shapeY, TetrisGrid.CellShapeA)) { - double newEvaluation = EvaluateWorkingGrid(); + double newEvaluation = _workGrid0.Evaluate(); _columnEvaluation[x] = newEvaluation; } else @@ -162,15 +162,6 @@ namespace VAR.ScreenAutomation.Bots } } - private double EvaluateWorkingGrid() - { - return _workGrid0.Evaluate( - aggregateHeightWeight: -0.510066, - completeLinesWeight: 0.760666, - holesWeight: -0.35663, - bumpinessWeight: -0.184483); - } - private const int ShotCooldownFrames = 2; private int _shotCooldown = ShotCooldownFrames; @@ -748,7 +739,11 @@ namespace VAR.ScreenAutomation.Bots return complete; } - public double Evaluate(double aggregateHeightWeight, double completeLinesWeight, double holesWeight, double bumpinessWeight) + public double Evaluate( + double aggregateHeightWeight = -0.510066, + double completeLinesWeight = 0.760666, + double holesWeight = -0.35663, + double bumpinessWeight = -0.184483) { // Calculte aggregate height for (int i = 0; i < _gridWidth; i++) From 7660b9ce886dbeefafe83d604b662105b2be32a5 Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Sun, 10 Nov 2019 18:36:08 +0100 Subject: [PATCH 24/26] TetrisBot: Make ShotCooldownFrames configurable. --- VAR.ScreenAutomation/Bots/TetrisBot.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/VAR.ScreenAutomation/Bots/TetrisBot.cs b/VAR.ScreenAutomation/Bots/TetrisBot.cs index efa2d83..3617898 100644 --- a/VAR.ScreenAutomation/Bots/TetrisBot.cs +++ b/VAR.ScreenAutomation/Bots/TetrisBot.cs @@ -28,12 +28,14 @@ namespace VAR.ScreenAutomation.Bots private const int DefaultGridWidth = 10; private const int DefaultGridHeight = 20; + private const int DefaultShotCooldownFrames = 2; public IConfiguration GetDefaultConfiguration() { var defaultConfiguration = new MemoryBackedConfiguration(); defaultConfiguration.Set("GridWidth", DefaultGridWidth); defaultConfiguration.Set("GridHeight", DefaultGridHeight); + defaultConfiguration.Set("ShotCooldownFrames", DefaultShotCooldownFrames); return defaultConfiguration; } @@ -41,6 +43,7 @@ namespace VAR.ScreenAutomation.Bots { int gridWidth = config.Get("GridWidth", DefaultGridWidth); int gridHeight = config.Get("GridHeight", DefaultGridHeight); + _shotCooldownFrames = config.Get("ShotCooldownFrames", DefaultShotCooldownFrames); _grid = new TetrisGrid(gridWidth, gridHeight); _workGrid0 = new TetrisGrid(gridWidth, gridHeight); @@ -162,8 +165,8 @@ namespace VAR.ScreenAutomation.Bots } } - private const int ShotCooldownFrames = 2; - private int _shotCooldown = ShotCooldownFrames; + private int _shotCooldownFrames = 0; + private int _shotCooldown = 0; public string ResponseKeys() { @@ -173,7 +176,7 @@ namespace VAR.ScreenAutomation.Bots { if (_shotCooldown <= 0) { - _shotCooldown = ShotCooldownFrames; + _shotCooldown = _shotCooldownFrames; return " "; } else @@ -183,7 +186,7 @@ namespace VAR.ScreenAutomation.Bots } } - _shotCooldown = ShotCooldownFrames; + _shotCooldown = _shotCooldownFrames; if (_bestRotation != 0 && _bestXOffset < 0) { return "{UP}{LEFT}"; } if (_bestRotation != 0 && _bestXOffset > 0) { return "{UP}{RIGHT}"; } @@ -837,6 +840,5 @@ namespace VAR.ScreenAutomation.Bots } } } - } } From 73e7fe89f18c898be06d3e09da2b982d6f09cc13 Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Sun, 10 Nov 2019 19:56:27 +0100 Subject: [PATCH 25/26] FrmScreenAutomation: Remove TopLevel when showing a FrmAutomationBotParams. --- VAR.ScreenAutomation/FrmScreenAutomation.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.cs b/VAR.ScreenAutomation/FrmScreenAutomation.cs index 058a2c4..4f99ba6 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.cs +++ b/VAR.ScreenAutomation/FrmScreenAutomation.cs @@ -131,8 +131,16 @@ namespace VAR.ScreenAutomation { StartPosition = FormStartPosition.CenterParent }; + if (_isToplevel) + { + WindowHandling.WindowSetTopLevel(this, false); + } frmAutomationBotParameters.ShowDialog(); InitBot(_automationBot.Name); + if (_isToplevel) + { + WindowHandling.WindowSetTopLevel(this); + } } private void Start() From fc5b81014bc2513524dcf04c63b3ff116c03fb61 Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Sat, 9 Apr 2022 00:55:02 +0200 Subject: [PATCH 26/26] Changes recommended by Rider/Resharper --- .../.idea/.gitignore | 13 + .../.idea/encodings.xml | 4 + .../.idea/indexLayout.xml | 8 + .../.idea.VAR.ScreenAutomation/.idea/vcs.xml | 6 + VAR.ScreenAutomation.sln.DotSettings | 60 ++++ VAR.ScreenAutomation/App.config | 4 +- VAR.ScreenAutomation/Bots/DummyBot.cs | 2 +- VAR.ScreenAutomation/Bots/TetrisBot.cs | 333 ++++++++++-------- .../Code/AutomationBotFactory.cs | 17 +- .../Code/FileBackedConfiguration.cs | 40 +-- .../Code/MemoryBackedConfiguration.cs | 25 +- VAR.ScreenAutomation/Code/Mouse.cs | 20 +- VAR.ScreenAutomation/Code/Screenshoter.cs | 20 +- VAR.ScreenAutomation/Code/WindowHandling.cs | 21 +- .../Controls/CtrImageViewer.cs | 16 +- VAR.ScreenAutomation/Controls/CtrOutput.cs | 16 +- .../FrmAutomationBotParams.cs | 36 +- .../FrmAutomationBotParams.resx | 235 ++++++------ VAR.ScreenAutomation/FrmScreenAutomation.cs | 75 ++-- VAR.ScreenAutomation/FrmScreenAutomation.resx | 235 ++++++------ .../Interfaces/IAutomationBot.cs | 2 +- .../Interfaces/IOutputHandler.cs | 2 +- VAR.ScreenAutomation/Program.cs | 2 +- .../Properties/AssemblyInfo.cs | 3 +- .../Properties/Resources.resx | 229 ++++++------ .../Properties/Settings.settings | 8 +- .../VAR.ScreenAutomation.csproj | 220 ++++++------ VAR.ScreenAutomation/app.manifest | 120 +++---- 28 files changed, 982 insertions(+), 790 deletions(-) create mode 100644 .idea/.idea.VAR.ScreenAutomation/.idea/.gitignore create mode 100644 .idea/.idea.VAR.ScreenAutomation/.idea/encodings.xml create mode 100644 .idea/.idea.VAR.ScreenAutomation/.idea/indexLayout.xml create mode 100644 .idea/.idea.VAR.ScreenAutomation/.idea/vcs.xml create mode 100644 VAR.ScreenAutomation.sln.DotSettings diff --git a/.idea/.idea.VAR.ScreenAutomation/.idea/.gitignore b/.idea/.idea.VAR.ScreenAutomation/.idea/.gitignore new file mode 100644 index 0000000..c516e08 --- /dev/null +++ b/.idea/.idea.VAR.ScreenAutomation/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/projectSettingsUpdater.xml +/.idea.VAR.ScreenAutomation.iml +/contentModel.xml +/modules.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.VAR.ScreenAutomation/.idea/encodings.xml b/.idea/.idea.VAR.ScreenAutomation/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.VAR.ScreenAutomation/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.VAR.ScreenAutomation/.idea/indexLayout.xml b/.idea/.idea.VAR.ScreenAutomation/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.VAR.ScreenAutomation/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.VAR.ScreenAutomation/.idea/vcs.xml b/.idea/.idea.VAR.ScreenAutomation/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/.idea.VAR.ScreenAutomation/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/VAR.ScreenAutomation.sln.DotSettings b/VAR.ScreenAutomation.sln.DotSettings new file mode 100644 index 0000000..33636c2 --- /dev/null +++ b/VAR.ScreenAutomation.sln.DotSettings @@ -0,0 +1,60 @@ + + NotRequired + True + True + TOGETHER_SAME_LINE + True + True + False + True + True + True + OneStep + OneStep + True + UseVarWhenEvident + UseVarWhenEvident + AES + AM + AUX + BPP + CYC + DC + DES + EPM + GDI + ID + IP + RECT + RGB + SCART + SPDIF + SQL + SRCCOPY + TCP + URL + USB + VAR + WMIC + YRYBY + False + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb"><ExtraRule Prefix="T" Suffix="" Style="AaBb_AaBb" /></Policy> + <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb_AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb_AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb_AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb_AaBb" /> + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb_AaBb" /> + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb_AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + True + True + True + True + \ No newline at end of file diff --git a/VAR.ScreenAutomation/App.config b/VAR.ScreenAutomation/App.config index 56efbc7..7597626 100644 --- a/VAR.ScreenAutomation/App.config +++ b/VAR.ScreenAutomation/App.config @@ -1,6 +1,6 @@  - - + + \ No newline at end of file diff --git a/VAR.ScreenAutomation/Bots/DummyBot.cs b/VAR.ScreenAutomation/Bots/DummyBot.cs index 7f263f5..300a60b 100644 --- a/VAR.ScreenAutomation/Bots/DummyBot.cs +++ b/VAR.ScreenAutomation/Bots/DummyBot.cs @@ -29,4 +29,4 @@ namespace VAR.ScreenAutomation.Bots return string.Empty; } } -} +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/Bots/TetrisBot.cs b/VAR.ScreenAutomation/Bots/TetrisBot.cs index 3617898..c6b0007 100644 --- a/VAR.ScreenAutomation/Bots/TetrisBot.cs +++ b/VAR.ScreenAutomation/Bots/TetrisBot.cs @@ -17,12 +17,12 @@ namespace VAR.ScreenAutomation.Bots private TetrisGrid _workGrid1; private double[] _columnEvaluation; - private bool _shapeFound = false; + private bool _shapeFound; private int _shapeX; private int _shapeY; private double _bestEvaluation = double.MinValue; - private int _bestXOffset = 0; - private int _bestRotation = 0; + private int _bestXOffset; + private int _bestRotation; public string Name => "Tetris"; @@ -57,7 +57,7 @@ namespace VAR.ScreenAutomation.Bots new TetrisShape(), }; output.Clean(); - output.AddLine(string.Format("TetrisBot: Starting {0}", DateTime.UtcNow.ToString("s"))); + output.AddLine($"TetrisBot: Starting {DateTime.UtcNow:s}"); } public Bitmap Process(Bitmap bmpInput, IOutputHandler output) @@ -74,6 +74,7 @@ namespace VAR.ScreenAutomation.Bots _currentShape[_bestRotation].Drop(_workGrid0, _shapeX + _bestXOffset, _shapeY, TetrisGrid.CellShapeA); _currentShape[0].PutOnGrid(_workGrid0, _shapeX, _shapeY, TetrisGrid.CellShapeB); } + _workGrid0.Draw(bmpInput, 0.75f); _workGrid0.SampleOther(_grid, TetrisGrid.CellSolid, TetrisGrid.CellSolid); _workGrid0.RemoveGround(); @@ -91,7 +92,8 @@ namespace VAR.ScreenAutomation.Bots { for (int x = 0; x < _grid.Width; x++) { - TetrisShape matchedShape = TetrisShape.DefaultShapes.FirstOrDefault(s => s.MatchOnGrid(_workGrid0, x, y)); + TetrisShape matchedShape = + TetrisShape.DefaultShapes.FirstOrDefault(s => s.MatchOnGrid(_workGrid0, x, y)); if (matchedShape != null) { _workGrid1.SampleOther(_workGrid0, TetrisGrid.CellSolid, TetrisGrid.CellSolid); @@ -102,14 +104,16 @@ namespace VAR.ScreenAutomation.Bots _currentShape[0].Copy(matchedShape); for (int i = 1; i < 4; i++) { - _currentShape[i].RotateCW(_currentShape[i - 1]); + _currentShape[i].RotateClockWise(_currentShape[i - 1]); } + _shapeX = x; _shapeY = y; _shapeFound = true; break; } } + if (_shapeFound) { break; } } } @@ -147,9 +151,11 @@ namespace VAR.ScreenAutomation.Bots // Search valid X range int minX = _shapeX; while (minX >= 0 && _columnEvaluation[minX] > double.MinValue) { minX--; } + minX++; int maxX = _shapeX; while (maxX < _grid.Width && _columnEvaluation[maxX] > double.MinValue) { maxX++; } + maxX--; // Apply best value inside valid X range @@ -165,8 +171,8 @@ namespace VAR.ScreenAutomation.Bots } } - private int _shotCooldownFrames = 0; - private int _shotCooldown = 0; + private int _shotCooldownFrames; + private int _shotCooldown; public string ResponseKeys() { @@ -189,9 +195,13 @@ namespace VAR.ScreenAutomation.Bots _shotCooldown = _shotCooldownFrames; if (_bestRotation != 0 && _bestXOffset < 0) { return "{UP}{LEFT}"; } + if (_bestRotation != 0 && _bestXOffset > 0) { return "{UP}{RIGHT}"; } + if (_bestRotation != 0) { return "{UP}"; } + if (_bestXOffset < 0) { return "{LEFT}"; } + if (_bestXOffset > 0) { return "{RIGHT}"; } return string.Empty; @@ -200,11 +210,11 @@ namespace VAR.ScreenAutomation.Bots public class TetrisShape { - public const int ShapeSize = 4; + private const int ShapeSize = 4; - private byte[][] _cells = null; + private readonly byte[][] _cells; - private int _count = 0; + private int _count; public TetrisShape(byte[][] cells = null) { @@ -220,9 +230,11 @@ namespace VAR.ScreenAutomation.Bots for (int j = 0; j < ShapeSize; j++) { if (j >= cells.Length) { break; } + for (int i = 0; i < ShapeSize; i++) { if (i >= cells[j].Length) { break; } + _cells[j][i] = cells[j][i]; if (_cells[j][i] != 0) { _count++; } } @@ -235,124 +247,140 @@ namespace VAR.ScreenAutomation.Bots return _count; } - private static List _defaultShapes = null; + private static List _defaultShapes; public static List DefaultShapes { get { - if (_defaultShapes == null) + return _defaultShapes ?? (_defaultShapes = new List { - _defaultShapes = new List + // I + new TetrisShape(new[] { - // I - new TetrisShape(new byte[][]{ - new byte[]{ 1, 1, 1, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, }, - new byte[]{ 1, }, - new byte[]{ 1, }, - new byte[]{ 1, }, - }), + new byte[] { 1, 1, 1, 1, }, + }), + new TetrisShape(new[] + { + new byte[] { 1, }, + new byte[] { 1, }, + new byte[] { 1, }, + new byte[] { 1, }, + }), - // J - new TetrisShape(new byte[][]{ - new byte[]{ 1, }, - new byte[]{ 1, 1, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, 1, }, - new byte[]{ 1, }, - new byte[]{ 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, 1, 1, }, - new byte[]{ 0, 0, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 0, 1, }, - new byte[]{ 0, 1, }, - new byte[]{ 1, 1, }, - }), + // J + new TetrisShape(new[] + { + new byte[] { 1, }, + new byte[] { 1, 1, 1, }, + }), + new TetrisShape(new[] + { + new byte[] { 1, 1, }, + new byte[] { 1, }, + new byte[] { 1, }, + }), + new TetrisShape(new[] + { + new byte[] { 1, 1, 1, }, + new byte[] { 0, 0, 1, }, + }), + new TetrisShape(new[] + { + new byte[] { 0, 1, }, + new byte[] { 0, 1, }, + new byte[] { 1, 1, }, + }), - // L - new TetrisShape(new byte[][]{ - new byte[]{ 0, 0, 1, }, - new byte[]{ 1, 1, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, }, - new byte[]{ 1, }, - new byte[]{ 1, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, 1, 1, }, - new byte[]{ 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, 1, }, - new byte[]{ 0, 1, }, - new byte[]{ 0, 1, }, - }), + // L + new TetrisShape(new[] + { + new byte[] { 0, 0, 1, }, + new byte[] { 1, 1, 1, }, + }), + new TetrisShape(new[] + { + new byte[] { 1, }, + new byte[] { 1, }, + new byte[] { 1, 1, }, + }), + new TetrisShape(new[] + { + new byte[] { 1, 1, 1, }, + new byte[] { 1, }, + }), + new TetrisShape(new[] + { + new byte[] { 1, 1, }, + new byte[] { 0, 1, }, + new byte[] { 0, 1, }, + }), - // S - new TetrisShape(new byte[][]{ - new byte[]{ 0, 1, 1, }, - new byte[]{ 1, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, }, - new byte[]{ 1, 1, }, - new byte[]{ 0, 1, }, - }), + // S + new TetrisShape(new[] + { + new byte[] { 0, 1, 1, }, + new byte[] { 1, 1, }, + }), + new TetrisShape(new[] + { + new byte[] { 1, }, + new byte[] { 1, 1, }, + new byte[] { 0, 1, }, + }), - // T - new TetrisShape(new byte[][]{ - new byte[]{ 0, 1, }, - new byte[]{ 1, 1, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, }, - new byte[]{ 1, 1, }, - new byte[]{ 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 1, 1, 1, }, - new byte[]{ 0, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 0, 1, }, - new byte[]{ 1, 1, }, - new byte[]{ 0, 1, }, - }), + // T + new TetrisShape(new[] + { + new byte[] { 0, 1, }, + new byte[] { 1, 1, 1, }, + }), + new TetrisShape(new[] + { + new byte[] { 1, }, + new byte[] { 1, 1, }, + new byte[] { 1, }, + }), + new TetrisShape(new[] + { + new byte[] { 1, 1, 1, }, + new byte[] { 0, 1, }, + }), + new TetrisShape(new[] + { + new byte[] { 0, 1, }, + new byte[] { 1, 1, }, + new byte[] { 0, 1, }, + }), - // Z - new TetrisShape(new byte[][]{ - new byte[]{ 1, 1, }, - new byte[]{ 0, 1, 1, }, - }), - new TetrisShape(new byte[][]{ - new byte[]{ 0, 1, }, - new byte[]{ 1, 1, }, - new byte[]{ 1, }, - }), + // Z + new TetrisShape(new[] + { + new byte[] { 1, 1, }, + new byte[] { 0, 1, 1, }, + }), + new TetrisShape(new[] + { + new byte[] { 0, 1, }, + new byte[] { 1, 1, }, + new byte[] { 1, }, + }), - // O - new TetrisShape(new byte[][]{ - new byte[]{ 1, 1, }, - new byte[]{ 1, 1, }, - }) - }; - } - return _defaultShapes; + // O + new TetrisShape(new[] + { + new byte[] { 1, 1, }, + new byte[] { 1, 1, }, + }) + }); } } public bool IsValid() { if (_count != 4) { return false; } - bool matchesAnyDefault = DefaultShapes.Any(ts => CompareShape(ts)); + + bool matchesAnyDefault = DefaultShapes.Any(CompareShape); return matchesAnyDefault; } @@ -368,7 +396,7 @@ namespace VAR.ScreenAutomation.Bots } } - public bool CompareShape(TetrisShape shape) + private bool CompareShape(TetrisShape shape) { for (int j = 0; j < ShapeSize; j++) { @@ -380,6 +408,7 @@ namespace VAR.ScreenAutomation.Bots } } } + return true; } @@ -410,6 +439,7 @@ namespace VAR.ScreenAutomation.Bots for (int i = 0; i < ShapeSize; i++) { if (_cells[j][i] == 0) { continue; } + grid.Set(x + i, y + j, value); } } @@ -422,27 +452,31 @@ namespace VAR.ScreenAutomation.Bots for (int i = 0; i < ShapeSize; i++) { if (_cells[j][i] == 0) { continue; } + if (grid.Get(x + i, y + j) != 0) { return true; } } } + return false; } public bool Drop(TetrisGrid grid, int x, int y, byte value) { if (CheckIntersection(grid, x, y)) { return false; } + while (CheckIntersection(grid, x, y + 1) == false) { y++; } + PutOnGrid(grid, x, y, value); return true; } - public bool SearchFirstCell(byte value, out int x, out int y) + private bool SearchFirstCell(byte value, out int x, out int y) { x = -1; y = -1; @@ -456,7 +490,9 @@ namespace VAR.ScreenAutomation.Bots } } } + if (y == -1) { return false; } + for (int i = 0; i < ShapeSize && x == -1; i++) { for (int j = 0; j < ShapeSize && x == -1; j++) @@ -467,11 +503,13 @@ namespace VAR.ScreenAutomation.Bots } } } + if (x == -1) { return false; } + return true; } - public void Offset(int x, int y) + private void Offset(int x, int y) { _count = 0; for (int j = 0; j < ShapeSize; j++) @@ -494,7 +532,7 @@ namespace VAR.ScreenAutomation.Bots } } - public void RotateCW(TetrisShape shape) + public void RotateClockWise(TetrisShape shape) { for (int j = 0; j < ShapeSize; j++) { @@ -503,6 +541,7 @@ namespace VAR.ScreenAutomation.Bots _cells[i][ShapeSize - (j + 1)] = shape._cells[j][i]; } } + _count = shape._count; if (SearchFirstCell(1, out int offsetX, out int offsetY)) @@ -518,15 +557,9 @@ namespace VAR.ScreenAutomation.Bots StringBuilder sbLine = new StringBuilder(); for (int x = 0; x < ShapeSize; x++) { - if (_cells[y][x] == 0) - { - sbLine.Append(".."); - } - else - { - sbLine.Append("[]"); - } + sbLine.Append(_cells[y][x] == 0 ? ".." : "[]"); } + output.AddLine(sbLine.ToString()); } } @@ -554,6 +587,7 @@ namespace VAR.ScreenAutomation.Bots } } } + return true; } } @@ -565,15 +599,15 @@ namespace VAR.ScreenAutomation.Bots public const byte CellShapeA = 2; public const byte CellShapeB = 3; - private int _gridWidth; - private int _gridHeight; + private readonly int _gridWidth; + private readonly int _gridHeight; - public int Width { get { return _gridWidth; } } - public int Height { get { return _gridHeight; } } + public int Width => _gridWidth; + public int Height => _gridHeight; - private byte[][] _grid = null; + private readonly byte[][] _grid; - private int[] _heights = null; + private readonly int[] _heights; public TetrisGrid(int gridWidth, int gridHeight) { @@ -584,27 +618,32 @@ namespace VAR.ScreenAutomation.Bots { _grid[y] = new byte[_gridWidth]; } + _heights = new int[_gridWidth]; } public byte Get(int x, int y) { if (x >= _gridWidth || x < 0) { return 0xFF; } + if (y >= _gridHeight || y < 0) { return 0xFF; } + return _grid[y][x]; } public void Set(int x, int y, byte value) { if (x >= _gridWidth || x < 0) { return; } + if (y >= _gridHeight || y < 0) { return; } + _grid[y][x] = value; } public void SampleFromBitmap(Bitmap bmp) { - float xStep = bmp.Width / _gridWidth; - float yStep = bmp.Height / _gridHeight; + float xStep = bmp.Width / (float)_gridWidth; + float yStep = bmp.Height / (float)_gridHeight; float xOff0 = xStep / 2; float xOff1 = xOff0 / 2; float xOff2 = xOff0 + xOff1; @@ -665,11 +704,14 @@ namespace VAR.ScreenAutomation.Bots } } - public void FloodFill(int x, int y, byte expectedValue, byte fillValue) + private void FloodFill(int x, int y, byte expectedValue, byte fillValue) { if (x >= _gridWidth || x < 0) { return; } + if (y >= _gridHeight || y < 0) { return; } + if (_grid[y][x] != expectedValue) { return; } + _grid[y][x] = fillValue; FloodFill(x - 1, y - 1, expectedValue, fillValue); FloodFill(x - 1, y + 0, expectedValue, fillValue); @@ -681,7 +723,7 @@ namespace VAR.ScreenAutomation.Bots FloodFill(x + 1, y + 1, expectedValue, fillValue); } - public void SampleOther(TetrisGrid grid, byte value, byte setValue = 1) + public void SampleOther(TetrisGrid grid, byte value, byte setValue) { for (int y = 0; y < _gridHeight; y++) { @@ -713,7 +755,9 @@ namespace VAR.ScreenAutomation.Bots } } } + if (y == -1) { return false; } + for (int i = 0; i < _gridWidth && x == -1; i++) { for (int j = 0; j < _gridHeight && x == -1; j++) @@ -724,11 +768,13 @@ namespace VAR.ScreenAutomation.Bots } } } + if (x == -1) { return false; } + return true; } - public bool IsCompleteLine(int y) + private bool IsCompleteLine(int y) { bool complete = true; for (int x = 0; x < _gridWidth; x++) @@ -739,6 +785,7 @@ namespace VAR.ScreenAutomation.Bots break; } } + return complete; } @@ -748,14 +795,16 @@ namespace VAR.ScreenAutomation.Bots double holesWeight = -0.35663, double bumpinessWeight = -0.184483) { - // Calculte aggregate height + // Calculate aggregate height for (int i = 0; i < _gridWidth; i++) { int j = 0; while (j < _gridHeight && _grid[j][i] == CellEmpty) { j++; } + _heights[i] = _gridHeight - j; } - double agregateHeight = _heights.Sum(); + + double aggregateHeight = _heights.Sum(); // Calculate complete lines int completeLines = 0; @@ -783,18 +832,18 @@ namespace VAR.ScreenAutomation.Bots } // Calculate bumpiness - int bumpines = 0; + int bumpiness = 0; for (int i = 1; i < _gridWidth; i++) { - bumpines += Math.Abs(_heights[i] - _heights[i - 1]); + bumpiness += Math.Abs(_heights[i] - _heights[i - 1]); } // Evaluate formula double evaluationValue = - aggregateHeightWeight * agregateHeight + + aggregateHeightWeight * aggregateHeight + completeLinesWeight * completeLines + holesWeight * holes + - bumpinessWeight * bumpines + + bumpinessWeight * bumpiness + 0; return evaluationValue; } @@ -832,13 +881,15 @@ namespace VAR.ScreenAutomation.Bots { br = Brushes.Green; } + if (br == null) { continue; } - g.DrawRectangle(borderPen, (xStep * x) + offX - 1, (yStep * y) + offY - 1, halfXStep + 2, halfYStep + 2); + g.DrawRectangle(borderPen, (xStep * x) + offX - 1, (yStep * y) + offY - 1, halfXStep + 2, + halfYStep + 2); g.FillRectangle(br, (xStep * x) + offX, (yStep * y) + offY, halfXStep, halfYStep); } } } } } -} +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/Code/AutomationBotFactory.cs b/VAR.ScreenAutomation/Code/AutomationBotFactory.cs index 2d4277a..c5ee5cc 100644 --- a/VAR.ScreenAutomation/Code/AutomationBotFactory.cs +++ b/VAR.ScreenAutomation/Code/AutomationBotFactory.cs @@ -6,9 +6,9 @@ using VAR.ScreenAutomation.Interfaces; namespace VAR.ScreenAutomation.Code { - public class AutomationBotFactory + public static class AutomationBotFactory { - private static Dictionary _dictAutomationBots = null; + private static Dictionary _dictAutomationBots; private static Dictionary GetDict() { @@ -28,8 +28,9 @@ namespace VAR.ScreenAutomation.Code true); _dictAutomationBots = automationBotTypes.ToDictionary(t => { - IAutomationBot automationBot = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(t) as IAutomationBot; - return automationBot.Name; + IAutomationBot automationBot = + System.Runtime.Serialization.FormatterServices.GetUninitializedObject(t) as IAutomationBot; + return automationBot?.Name ?? t.Name; }); return _dictAutomationBots; @@ -39,7 +40,7 @@ namespace VAR.ScreenAutomation.Code { Dictionary dict = GetDict(); string[] allAutomationBots = dict.Select(p => p.Key).ToArray(); - return allAutomationBots; + return allAutomationBots.ToArray(); } public static IAutomationBot CreateFromName(string name) @@ -49,10 +50,12 @@ namespace VAR.ScreenAutomation.Code { return new DummyBot(); } + if (dict.ContainsKey(name) == false) { - throw new NotImplementedException(string.Format("Can't create IAutomationBot with this name: {0}", name)); + throw new NotImplementedException($"Can't create IAutomationBot with this name: {name}"); } + Type proxyCmdExecutorType = dict[name]; IAutomationBot automationBot = Activator.CreateInstance(proxyCmdExecutorType) as IAutomationBot; @@ -60,4 +63,4 @@ namespace VAR.ScreenAutomation.Code return automationBot; } } -} +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/Code/FileBackedConfiguration.cs b/VAR.ScreenAutomation/Code/FileBackedConfiguration.cs index a845532..31d092b 100644 --- a/VAR.ScreenAutomation/Code/FileBackedConfiguration.cs +++ b/VAR.ScreenAutomation/Code/FileBackedConfiguration.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Text; using VAR.ScreenAutomation.Interfaces; @@ -7,9 +8,9 @@ namespace VAR.ScreenAutomation.Code { public class FileBackedConfiguration : IConfiguration { - private MemoryBackedConfiguration _config = new MemoryBackedConfiguration(); + private readonly MemoryBackedConfiguration _config = new MemoryBackedConfiguration(); - private string _name = null; + private readonly string _name; public FileBackedConfiguration(string name = null) { @@ -18,33 +19,22 @@ namespace VAR.ScreenAutomation.Code private static string GetConfigFileName(string name = null) { - string location = System.Reflection.Assembly.GetEntryAssembly().Location; + string location = System.Reflection.Assembly.GetEntryAssembly()?.Location; string path = Path.GetDirectoryName(location); string filenameWithoutExtension = Path.GetFileNameWithoutExtension(location); - string configFile; - if (string.IsNullOrEmpty(name)) - { - configFile = string.Format("{0}/{1}.cfg", path, filenameWithoutExtension); - } - else - { - configFile = string.Format("{0}/{1}_{2}.cfg", path, filenameWithoutExtension, name); - } + string configFile = string.IsNullOrEmpty(name) + ? $"{path}/{filenameWithoutExtension}.cfg" + : $"{path}/{filenameWithoutExtension}_{name}.cfg"; return configFile; } private static string[] GetConfigurationLines(string name = null) { string configFile = GetConfigFileName(name); - string[] config; - if (File.Exists(configFile) == false) - { - config = new string[0]; - } - else - { - config = File.ReadAllLines(configFile); - } + string[] config = File.Exists(configFile) == false + ? Array.Empty() + : File.ReadAllLines(configFile); + return config; } @@ -58,11 +48,13 @@ namespace VAR.ScreenAutomation.Code _config.Set(key, other.Get(key, null)); } } + string[] configLines = GetConfigurationLines(_name); foreach (string configLine in configLines) { int idxSplit = configLine.IndexOf('|'); if (idxSplit < 0) { continue; } + string configName = configLine.Substring(0, idxSplit); string configData = configLine.Substring(idxSplit + 1); @@ -77,6 +69,7 @@ namespace VAR.ScreenAutomation.Code { sbConfig.AppendFormat("{0}|{1}\n", key, _config.Get(key, string.Empty)); } + string configFileName = GetConfigFileName(_name); File.WriteAllText(configFileName, sbConfig.ToString()); } @@ -120,6 +113,5 @@ namespace VAR.ScreenAutomation.Code { _config.Set(key, value); } - } -} +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/Code/MemoryBackedConfiguration.cs b/VAR.ScreenAutomation/Code/MemoryBackedConfiguration.cs index 140313a..a7ab978 100644 --- a/VAR.ScreenAutomation/Code/MemoryBackedConfiguration.cs +++ b/VAR.ScreenAutomation/Code/MemoryBackedConfiguration.cs @@ -7,12 +7,13 @@ namespace VAR.ScreenAutomation.Code { public class MemoryBackedConfiguration : IConfiguration { - private Dictionary _configItems = new Dictionary(); + private readonly Dictionary _configItems = new Dictionary(); public IEnumerable GetKeys() { - if (_configItems == null) { return new List(); } - return _configItems.Select(p => p.Key); + return _configItems == null + ? new List() + : _configItems.Select(p => p.Key); } public void Clear() @@ -23,41 +24,44 @@ namespace VAR.ScreenAutomation.Code public string Get(string key, string defaultValue) { if (_configItems == null) { return defaultValue; } - if (_configItems.ContainsKey(key)) - { - return _configItems[key]; - } - return defaultValue; + + return _configItems.ContainsKey(key) ? _configItems[key] : defaultValue; } public int Get(string key, int defaultValue) { if (_configItems == null) { return defaultValue; } + if (_configItems.ContainsKey(key)) { if (int.TryParse(_configItems[key], out int value)) { return value; } + return defaultValue; } + return defaultValue; } public bool Get(string key, bool defaultValue) { if (_configItems == null) { return defaultValue; } + if (_configItems.ContainsKey(key)) { string value = _configItems[key]; return (value == "true"); } + return defaultValue; } public void Set(string key, string value) { if (_configItems == null) { return; } + if (_configItems.ContainsKey(key)) { _configItems[key] = value; @@ -71,6 +75,7 @@ namespace VAR.ScreenAutomation.Code public void Set(string key, int value) { if (_configItems == null) { return; } + if (_configItems.ContainsKey(key)) { _configItems[key] = Convert.ToString(value); @@ -84,6 +89,7 @@ namespace VAR.ScreenAutomation.Code public void Set(string key, bool value) { if (_configItems == null) { return; } + if (_configItems.ContainsKey(key)) { _configItems[key] = value ? "true" : "false"; @@ -93,6 +99,5 @@ namespace VAR.ScreenAutomation.Code _configItems.Add(key, value ? "true" : "false"); } } - } -} +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/Code/Mouse.cs b/VAR.ScreenAutomation/Code/Mouse.cs index 2c8cfcc..0cbe672 100644 --- a/VAR.ScreenAutomation/Code/Mouse.cs +++ b/VAR.ScreenAutomation/Code/Mouse.cs @@ -1,9 +1,11 @@ using System; using System.Runtime.InteropServices; +// ReSharper disable InconsistentNaming + namespace VAR.ScreenAutomation.Code { - public class Mouse + public static class Mouse { public enum MouseButtons { @@ -24,14 +26,17 @@ namespace VAR.ScreenAutomation.Code { input.Data.Mouse.Flags = down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; } + if (button == MouseButtons.Middle) { input.Data.Mouse.Flags = down ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP; } + if (button == MouseButtons.Right) { input.Data.Mouse.Flags = down ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; } + INPUT[] inputs = new INPUT[] { input }; if (SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT))) == 0) throw new Exception(); @@ -73,14 +78,11 @@ namespace VAR.ScreenAutomation.Code [StructLayout(LayoutKind.Explicit)] public struct MOUSEKEYBDHARDWAREINPUT { - [FieldOffset(0)] - public HARDWAREINPUT Hardware; + [FieldOffset(0)] public HARDWAREINPUT Hardware; - [FieldOffset(0)] - public KEYBDINPUT Keyboard; + [FieldOffset(0)] public KEYBDINPUT Keyboard; - [FieldOffset(0)] - public MOUSEINPUT Mouse; + [FieldOffset(0)] public MOUSEINPUT Mouse; } /// @@ -144,7 +146,6 @@ namespace VAR.ScreenAutomation.Code public static extern int SendInput(int nInputs, INPUT[] pInputs, int cbSize); - /// /// Struct representing a point. /// @@ -163,7 +164,6 @@ namespace VAR.ScreenAutomation.Code public static extern bool GetCursorPos(out POINT lpPoint); [DllImport("User32.dll")] - public static extern Boolean SetCursorPos(UInt32 X, UInt32 Y); - + public static extern Boolean SetCursorPos(UInt32 x, UInt32 y); } } \ No newline at end of file diff --git a/VAR.ScreenAutomation/Code/Screenshoter.cs b/VAR.ScreenAutomation/Code/Screenshoter.cs index 127e4ed..8003614 100644 --- a/VAR.ScreenAutomation/Code/Screenshoter.cs +++ b/VAR.ScreenAutomation/Code/Screenshoter.cs @@ -4,17 +4,19 @@ using System.Windows.Forms; namespace VAR.ScreenAutomation.Code { - public class Screenshoter + public static class Screenshoter { public static Bitmap CaptureControl(Control ctrl, Bitmap bmp = null) { if (ctrl == null) { return bmp; } + Point picCapturerOrigin = ctrl.PointToScreen(new Point(0, 0)); bmp = CaptureScreen(bmp, picCapturerOrigin.X, picCapturerOrigin.Y, ctrl.Width, ctrl.Height); return bmp; } - public static Bitmap CaptureScreen(Bitmap bmp = null, int? left = null, int? top = null, int? width = null, int? height = null) + private static Bitmap CaptureScreen(Bitmap bmp = null, int? left = null, int? top = null, int? width = null, + int? height = null) { if (width <= 0 || height <= 0) { return bmp; } @@ -25,9 +27,9 @@ namespace VAR.ScreenAutomation.Code height = height ?? SystemInformation.VirtualScreen.Height; // Create a bitmap of the appropriate size to receive the screenshot. - if (bmp == null || bmp?.Width != width || bmp?.Height != height) + if (bmp == null || bmp.Width != width || bmp.Height != height) { - bmp = new Bitmap(width ?? 0, height ?? 0); + bmp = new Bitmap((int)width, (int)height); } try @@ -35,11 +37,15 @@ namespace VAR.ScreenAutomation.Code // Draw the screenshot into our bitmap. using (Graphics g = Graphics.FromImage(bmp)) { - g.CopyFromScreen(left ?? 0, top ?? 0, 0, 0, bmp.Size); + g.CopyFromScreen((int)left, (int)top, 0, 0, bmp.Size); } } - catch (Exception) { /* Nom Nom Nom */} + catch (Exception) + { + /* Nom Nom Nom */ + } + return bmp; } } -} +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/Code/WindowHandling.cs b/VAR.ScreenAutomation/Code/WindowHandling.cs index bf5364a..fe9f5a4 100644 --- a/VAR.ScreenAutomation/Code/WindowHandling.cs +++ b/VAR.ScreenAutomation/Code/WindowHandling.cs @@ -3,9 +3,11 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; +// ReSharper disable InconsistentNaming + namespace VAR.ScreenAutomation.Code { - public class WindowHandling + public static class WindowHandling { private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); @@ -15,18 +17,15 @@ namespace VAR.ScreenAutomation.Code [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, + uint uFlags); public static void WindowSetTopLevel(Form form, bool top = true) { - if (top) - { - SetWindowPos(form.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS); - } - else - { - SetWindowPos(form.Handle, HWND_NOTOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS); - } + SetWindowPos(form.Handle, top + ? HWND_TOPMOST + : HWND_NOTOPMOST, + 0, 0, 0, 0, TOPMOST_FLAGS); } public static bool ApplicationIsActivated() @@ -48,4 +47,4 @@ namespace VAR.ScreenAutomation.Code [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern int GetWindowThreadProcessId(IntPtr handle, out int processId); } -} +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/Controls/CtrImageViewer.cs b/VAR.ScreenAutomation/Controls/CtrImageViewer.cs index 361a5eb..082787e 100644 --- a/VAR.ScreenAutomation/Controls/CtrImageViewer.cs +++ b/VAR.ScreenAutomation/Controls/CtrImageViewer.cs @@ -8,7 +8,7 @@ namespace VAR.ScreenAutomation.Controls { #region Declarations - private Image _imageShow = null; + private Image _imageShow; #endregion @@ -16,7 +16,8 @@ namespace VAR.ScreenAutomation.Controls public Image ImageShow { - get { return _imageShow; } + // ReSharper disable once InconsistentlySynchronizedField + get => _imageShow; set { lock (this) @@ -32,6 +33,11 @@ namespace VAR.ScreenAutomation.Controls #region Control life cycle public CtrImageViewer() + { + InitializeComponent(); + } + + private void InitializeComponent() { BackColor = Color.Black; } @@ -59,6 +65,7 @@ namespace VAR.ScreenAutomation.Controls { return; } + lock (_imageShow) { if (graph == null) @@ -71,7 +78,7 @@ namespace VAR.ScreenAutomation.Controls int imgDrawHeight; float imgDrawX = 0; float imgDrawY = 0; - float relation = (float)_imageShow.Width / (float)_imageShow.Height; + float relation = _imageShow.Width / (float)_imageShow.Height; if (relation > 0) { // Imagen mas ancha que alta @@ -110,6 +117,5 @@ namespace VAR.ScreenAutomation.Controls } #endregion - } -} +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/Controls/CtrOutput.cs b/VAR.ScreenAutomation/Controls/CtrOutput.cs index d64ce96..5889580 100644 --- a/VAR.ScreenAutomation/Controls/CtrOutput.cs +++ b/VAR.ScreenAutomation/Controls/CtrOutput.cs @@ -6,6 +6,8 @@ using System.Text; using System.Windows.Forms; using VAR.ScreenAutomation.Interfaces; +// ReSharper disable InconsistentNaming + namespace VAR.ScreenAutomation.Controls { public class CtrOutput : Control, IOutputHandler @@ -18,6 +20,7 @@ namespace VAR.ScreenAutomation.Controls { public string Text { get; set; } public object Data { get; set; } + public override string ToString() { return Text; @@ -37,7 +40,7 @@ namespace VAR.ScreenAutomation.Controls { Dock = DockStyle.Fill, FormattingEnabled = true, - Font = new System.Drawing.Font("Consolas", 9), + Font = new Font("Consolas", 9), BackColor = Color.Black, ForeColor = Color.Gray, SelectionMode = SelectionMode.MultiExtended, @@ -69,6 +72,7 @@ namespace VAR.ScreenAutomation.Controls CopyToClipboard(); return true; } + return base.ProcessCmdKey(ref msg, keyData); } @@ -87,6 +91,7 @@ namespace VAR.ScreenAutomation.Controls { sbText.AppendLine(item.Text); } + if (sbText.Length > 0) { Clipboard.SetText(sbText.ToString()); @@ -106,7 +111,7 @@ namespace VAR.ScreenAutomation.Controls } } - private bool _updated = false; + private bool _updated; private readonly List _pendingOutput = new List(); private void UpdatePosition() @@ -119,6 +124,7 @@ namespace VAR.ScreenAutomation.Controls { _listBox.Items.Add(item); } + _pendingOutput.Clear(); _listBox.ResumeLayout(); @@ -131,7 +137,7 @@ namespace VAR.ScreenAutomation.Controls } [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)] - private static extern IntPtr SendMessage(HandleRef hWnd, Int32 Msg, IntPtr wParam, IntPtr lParam); + private static extern IntPtr SendMessage(HandleRef hWnd, Int32 msg, IntPtr wParam, IntPtr lParam); private static void EnableRepaint(HandleRef handle, bool enable) { @@ -168,6 +174,7 @@ namespace VAR.ScreenAutomation.Controls public string GetCurrentText() { if (_listBox.SelectedItems.Count == 0) { return null; } + OutputItem item = (OutputItem)_listBox.SelectedItems[0]; return item?.Text; } @@ -175,8 +182,9 @@ namespace VAR.ScreenAutomation.Controls public object GetCurrentData() { if (_listBox.SelectedItems.Count == 0) { return null; } + OutputItem item = (OutputItem)_listBox.SelectedItems[0]; return item?.Data; } } -} +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/FrmAutomationBotParams.cs b/VAR.ScreenAutomation/FrmAutomationBotParams.cs index 2647ed8..9c3d16c 100644 --- a/VAR.ScreenAutomation/FrmAutomationBotParams.cs +++ b/VAR.ScreenAutomation/FrmAutomationBotParams.cs @@ -9,11 +9,11 @@ namespace VAR.ScreenAutomation { public partial class FrmAutomationBotParams : Form { - private FileBackedConfiguration _config = null; + private readonly FileBackedConfiguration _config; - private BindingList pairs = null; + private BindingList _pairs; - private DataGridView dgvParams = null; + private DataGridView _dgvParams; public FrmAutomationBotParams(FileBackedConfiguration config) { @@ -24,32 +24,34 @@ namespace VAR.ScreenAutomation private void FrmAutomationBotParams_Load(object sender, EventArgs e) { - pairs = new BindingList(); + _pairs = new BindingList(); foreach (string key in _config.GetKeys()) { - pairs.Add(new Pair { Key = key, Value = _config.Get(key, string.Empty), }); + _pairs.Add(new Pair { Key = key, Value = _config.Get(key, string.Empty), }); } - pairs.AddingNew += (s, a) => + + _pairs.AddingNew += (s, a) => { - a.NewObject = new Pair { Parent = pairs }; + a.NewObject = new Pair { Parent = _pairs }; }; - dgvParams = new DataGridView + _dgvParams = new DataGridView { Dock = DockStyle.Fill, - DataSource = pairs + DataSource = _pairs }; - Controls.Add(dgvParams); + Controls.Add(_dgvParams); } private void FrmAutomationBotParams_FormClosing(object sender, FormClosingEventArgs e) { - var parms = new Dictionary(); - foreach (Pair pair in pairs) + foreach (Pair pair in _pairs) { if (string.IsNullOrEmpty(pair.Key)) { continue; } + _config.Set(pair.Key, pair.Value); } + _config.Save(); } @@ -59,23 +61,21 @@ namespace VAR.ScreenAutomation public string Key { get; set; } public string Value { get; set; } - string IDataErrorInfo.Error - { - get { return string.Empty; } - } + string IDataErrorInfo.Error => string.Empty; string IDataErrorInfo.this[string columnName] { get { if (columnName == "Key" && Parent != null && Parent.Any( - x => x.Key == this.Key && !ReferenceEquals(x, this))) + x => x.Key == this.Key && !ReferenceEquals(x, this))) { return "duplicate key"; } + return string.Empty; } } } } -} +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/FrmAutomationBotParams.resx b/VAR.ScreenAutomation/FrmAutomationBotParams.resx index 1af7de1..d5a483d 100644 --- a/VAR.ScreenAutomation/FrmAutomationBotParams.resx +++ b/VAR.ScreenAutomation/FrmAutomationBotParams.resx @@ -1,120 +1,125 @@  - - - - - - - + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.cs b/VAR.ScreenAutomation/FrmScreenAutomation.cs index 4f99ba6..d85d534 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.cs +++ b/VAR.ScreenAutomation/FrmScreenAutomation.cs @@ -5,23 +5,31 @@ using System.Windows.Forms; using VAR.ScreenAutomation.Code; using VAR.ScreenAutomation.Interfaces; +// ReSharper disable LocalizableElement + namespace VAR.ScreenAutomation { public partial class FrmScreenAutomation : Form { - private bool _running = false; - private IAutomationBot _automationBot = null; + private bool _running; + private IAutomationBot _automationBot; - private Timer timTicker; - private Bitmap bmpScreen = null; + private Timer _timTicker; + private Bitmap _bmpScreen; public FrmScreenAutomation() { - AutoScaleMode = AutoScaleMode.None; - Font = new Font(Font.Name, 8.25f * 96f / CreateGraphics().DpiX, Font.Style, Font.Unit, Font.GdiCharSet, Font.GdiVerticalFont); + PreInitializeComponent(); InitializeComponent(); } + private void PreInitializeComponent() + { + AutoScaleMode = AutoScaleMode.None; + Font = new Font(Font.Name, 8.25f * 96f / CreateGraphics().DpiX, Font.Style, Font.Unit, Font.GdiCharSet, + Font.GdiVerticalFont); + } + private void FrmScreenAutomation_Load(object sender, EventArgs e) { var configuration = new FileBackedConfiguration(); @@ -31,7 +39,8 @@ namespace VAR.ScreenAutomation Width = configuration.Get("Width", Width); Height = configuration.Get("Height", Height); splitMain.SplitterDistance = configuration.Get("splitMain.SplitterDistance", splitMain.SplitterDistance); - splitOutput.SplitterDistance = configuration.Get("splitOutput.SplitterDistance", splitOutput.SplitterDistance); + splitOutput.SplitterDistance = + configuration.Get("splitOutput.SplitterDistance", splitOutput.SplitterDistance); SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.OptimizedDoubleBuffer, true); @@ -40,7 +49,8 @@ namespace VAR.ScreenAutomation picCapturer.BackColor = Color.LimeGreen; ddlAutomationBot.Items.AddRange(AutomationBotFactory.GetAllAutomationBots()); - ddlAutomationBot.SelectedItem = configuration.Get("ddlAutomationBot", (string)ddlAutomationBot.SelectedItem); + ddlAutomationBot.SelectedItem = + configuration.Get("ddlAutomationBot", (string)ddlAutomationBot.SelectedItem); if (ddlAutomationBot.SelectedIndex < 0) { ddlAutomationBot.SelectedIndex = 0; @@ -52,14 +62,14 @@ namespace VAR.ScreenAutomation chkClick.Checked = configuration.Get("chkClick", chkClick.Checked); if (components == null) { components = new Container(); } - timTicker = new Timer(components) + + _timTicker = new Timer(components) { Interval = Convert.ToInt32(1000 / numFPS.Value), }; - timTicker.Tick += TimTicker_Tick; - timTicker.Enabled = true; - timTicker.Start(); - + _timTicker.Tick += TimTicker_Tick; + _timTicker.Enabled = true; + _timTicker.Start(); } private void FrmScreenAutomation_FormClosing(object sender, FormClosingEventArgs e) @@ -80,14 +90,14 @@ namespace VAR.ScreenAutomation private void TimTicker_Tick(object sender, EventArgs e) { - timTicker.Enabled = false; - timTicker.Stop(); + _timTicker.Enabled = false; + _timTicker.Stop(); - bmpScreen = Screenshoter.CaptureControl(picCapturer, bmpScreen); + _bmpScreen = Screenshoter.CaptureControl(picCapturer, _bmpScreen); if (_automationBot != null) { - bmpScreen = _automationBot.Process(bmpScreen, ctrOutput); + _bmpScreen = _automationBot.Process(_bmpScreen, ctrOutput); if (_running) { string responseKeys = _automationBot.ResponseKeys(); @@ -97,11 +107,12 @@ namespace VAR.ScreenAutomation } } } - picPreview.ImageShow = bmpScreen; - timTicker.Interval = Convert.ToInt32(1000 / numFPS.Value); - timTicker.Enabled = true; - timTicker.Start(); + picPreview.ImageShow = _bmpScreen; + + _timTicker.Interval = Convert.ToInt32(1000 / numFPS.Value); + _timTicker.Enabled = true; + _timTicker.Start(); } private void BtnStartEnd_Click(object sender, EventArgs e) @@ -124,6 +135,7 @@ namespace VAR.ScreenAutomation private void BtnAutomationBotConfig_Click(object sender, EventArgs e) { if (_automationBot == null) { return; } + IConfiguration defaultConfig = _automationBot.GetDefaultConfiguration(); var config = new FileBackedConfiguration(_automationBot.Name); config.Load(defaultConfig); @@ -135,6 +147,7 @@ namespace VAR.ScreenAutomation { WindowHandling.WindowSetTopLevel(this, false); } + frmAutomationBotParameters.ShowDialog(); InitBot(_automationBot.Name); if (_isToplevel) @@ -146,20 +159,22 @@ namespace VAR.ScreenAutomation private void Start() { if (_running) { return; } + _running = true; btnStartEnd.Text = "End"; InitBot((string)ddlAutomationBot.SelectedItem); - if (chkClick.Checked) - { - Point pointCapturerCenter = picCapturer.PointToScreen(new Point(picCapturer.Width / 2, picCapturer.Height / 2)); - Mouse.SetPosition((uint)pointCapturerCenter.X, (uint)pointCapturerCenter.Y); - Mouse.Click(Mouse.MouseButtons.Left); - } + if (!chkClick.Checked) { return; } + + Point pointCapturerCenter = + picCapturer.PointToScreen(new Point(picCapturer.Width / 2, picCapturer.Height / 2)); + Mouse.SetPosition((uint)pointCapturerCenter.X, (uint)pointCapturerCenter.Y); + Mouse.Click(Mouse.MouseButtons.Left); } private void End() { if (_running == false) { return; } + _running = false; btnStartEnd.Text = "Start"; } @@ -177,22 +192,24 @@ namespace VAR.ScreenAutomation WindowSetTopLevel(chkKeepToplevel.Checked); } - private bool _isToplevel = false; + private bool _isToplevel; private void WindowSetTopLevel(bool toplevel) { if (toplevel) { if (_isToplevel) { return; } + WindowHandling.WindowSetTopLevel(this); _isToplevel = true; } else { if (_isToplevel == false) { return; } + WindowHandling.WindowSetTopLevel(this, false); _isToplevel = false; } } } -} +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/FrmScreenAutomation.resx b/VAR.ScreenAutomation/FrmScreenAutomation.resx index 1af7de1..d5a483d 100644 --- a/VAR.ScreenAutomation/FrmScreenAutomation.resx +++ b/VAR.ScreenAutomation/FrmScreenAutomation.resx @@ -1,120 +1,125 @@  - - - - - - - + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/VAR.ScreenAutomation/Interfaces/IAutomationBot.cs b/VAR.ScreenAutomation/Interfaces/IAutomationBot.cs index c546956..b4364e4 100644 --- a/VAR.ScreenAutomation/Interfaces/IAutomationBot.cs +++ b/VAR.ScreenAutomation/Interfaces/IAutomationBot.cs @@ -10,4 +10,4 @@ namespace VAR.ScreenAutomation.Interfaces Bitmap Process(Bitmap bmpInput, IOutputHandler output); string ResponseKeys(); } -} +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/Interfaces/IOutputHandler.cs b/VAR.ScreenAutomation/Interfaces/IOutputHandler.cs index b86c242..99002e6 100644 --- a/VAR.ScreenAutomation/Interfaces/IOutputHandler.cs +++ b/VAR.ScreenAutomation/Interfaces/IOutputHandler.cs @@ -5,4 +5,4 @@ void Clean(); void AddLine(string line, object data = null); } -} +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/Program.cs b/VAR.ScreenAutomation/Program.cs index 308f05b..62a2ead 100644 --- a/VAR.ScreenAutomation/Program.cs +++ b/VAR.ScreenAutomation/Program.cs @@ -16,4 +16,4 @@ namespace VAR.ScreenAutomation Application.Run(new FrmScreenAutomation()); } } -} +} \ No newline at end of file diff --git a/VAR.ScreenAutomation/Properties/AssemblyInfo.cs b/VAR.ScreenAutomation/Properties/AssemblyInfo.cs index 1f625a3..0ece480 100644 --- a/VAR.ScreenAutomation/Properties/AssemblyInfo.cs +++ b/VAR.ScreenAutomation/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -33,4 +32,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/VAR.ScreenAutomation/Properties/Resources.resx b/VAR.ScreenAutomation/Properties/Resources.resx index af7dbeb..1c875c9 100644 --- a/VAR.ScreenAutomation/Properties/Resources.resx +++ b/VAR.ScreenAutomation/Properties/Resources.resx @@ -1,117 +1,122 @@  - - - - - - + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/VAR.ScreenAutomation/Properties/Settings.settings b/VAR.ScreenAutomation/Properties/Settings.settings index 3964565..796d34b 100644 --- a/VAR.ScreenAutomation/Properties/Settings.settings +++ b/VAR.ScreenAutomation/Properties/Settings.settings @@ -1,7 +1,7 @@  - - - - + + + + diff --git a/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj b/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj index 16cb54a..905957e 100644 --- a/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj +++ b/VAR.ScreenAutomation/VAR.ScreenAutomation.csproj @@ -1,113 +1,113 @@  - - - Debug - AnyCPU - {E2BE8E2A-3422-42A6-82FA-5E0CCDC42032} - WinExe - VAR.ScreenAutomation - VAR.ScreenAutomation - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - app.manifest - - - - - - - - - - - - - - - - - - - - - - Form - - - FrmAutomationBotParams.cs - - - - - - - Component - - - Component - - - Form - - - FrmScreenAutomation.cs - - - - - - - FrmAutomationBotParams.cs - - - FrmScreenAutomation.cs - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - + + + Debug + AnyCPU + {E2BE8E2A-3422-42A6-82FA-5E0CCDC42032} + WinExe + VAR.ScreenAutomation + VAR.ScreenAutomation + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + app.manifest + + + + + + + + + + + + + + + + + + + + + + Form + + + FrmAutomationBotParams.cs + + + + + + + Component + + + Component + + + Form + + + FrmScreenAutomation.cs + + + + + + + FrmAutomationBotParams.cs + + + FrmScreenAutomation.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + \ No newline at end of file diff --git a/VAR.ScreenAutomation/app.manifest b/VAR.ScreenAutomation/app.manifest index 648dc43..d25cb99 100644 --- a/VAR.ScreenAutomation/app.manifest +++ b/VAR.ScreenAutomation/app.manifest @@ -1,76 +1,76 @@  - - - - - + + + + - - - + + + - Specifying requestedExecutionLevel element will disable file and registry virtualization. - Remove this element if your application requires this virtualization for backwards - compatibility. - --> - - - - + + - - - + + - - + + - - + + - - + + - - + + - - + + + + true + - - - - - - true - - - - - + +