Last active
March 4, 2022 20:40
-
-
Save rdavisau/b6e23ca79fe12b54de4e to your computer and use it in GitHub Desktop.
LINQPad 'internet is down' dino script - http://ryandavis.io/content/images/2016/03/the-internet-is-down.m4v
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Query Kind="Program"> | |
<Reference><RuntimeDirectory>\System.Windows.Forms.dll</Reference> | |
<Reference><RuntimeDirectory>\System.Security.dll</Reference> | |
<Reference><RuntimeDirectory>\System.Configuration.dll</Reference> | |
<Reference><RuntimeDirectory>\Accessibility.dll</Reference> | |
<Reference><RuntimeDirectory>\System.Runtime.Serialization.Formatters.Soap.dll</Reference> | |
<Reference><RuntimeDirectory>\System.Deployment.dll</Reference> | |
<NuGetReference>DynamicLINQ</NuGetReference> | |
<NuGetReference>DynamicQuery</NuGetReference> | |
<NuGetReference>Newtonsoft.Json</NuGetReference> | |
<NuGetReference>Rx-Main</NuGetReference> | |
<Namespace>System.Diagnostics</Namespace> | |
<Namespace>System.Drawing</Namespace> | |
<Namespace>System.Drawing.Imaging</Namespace> | |
<Namespace>System.Reactive</Namespace> | |
<Namespace>System.Reactive.Linq</Namespace> | |
<Namespace>System.Reactive.Subjects</Namespace> | |
<Namespace>System.Runtime.InteropServices</Namespace> | |
<Namespace>System.Windows.Forms</Namespace> | |
<Namespace>Newtonsoft.Json</Namespace> | |
<Namespace>System.Threading.Tasks</Namespace> | |
</Query> | |
/* | |
INSTRUCTIONS: | |
1. open chrome, get the dinosaur 'no internet' page happening | |
2. we need to tell the script where the dinosaur is; we use a highly technical approach - | |
putting the mouse just behind the bottom left* of the dinosaur and then pressing enter in LINQPad. | |
when the first if statement is `if (true)`, the script waits for you to press enter in the readline box and takes | |
the current cursor position. so the approach is | |
- press play on LINQPad, click inside the readline box | |
- move your cursor to the correct position behind the dinosaur | |
- press enter | |
that mouse position is saved so for as long as you don't move the chrome window you can change `if (true)` to `if (false)` | |
and it will keep using the old position. alternatively, experiment with different positions to improve the dinosaur's performance | |
3. that's pretty much it - once location is set the script will press space when it 'thinks' there's a threat nearby | |
4. it's not a very smart script | |
note: doesn't work with font scaling - i guess the dimensions change. some of the nugets/references probably aren't needed | |
* i 'think' it's the bottom left - if the boundaries around the dinosaur look like this, it is correct: http://i.imgur.com/choOOMU.png | |
*/ | |
[DllImport("user32.dll")] | |
static extern bool GetCursorPos(ref Point lpPoint); | |
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)] | |
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop); | |
async void Main() | |
{ | |
Point monitorLocation = new Point(); | |
if (true) | |
{ | |
Console.ReadLine(); | |
GetCursorPos(ref monitorLocation); | |
File.WriteAllText("cursor.txt", JsonConvert.SerializeObject(monitorLocation)); | |
} | |
else monitorLocation = JsonConvert.DeserializeObject<Point>(File.ReadAllText("cursor.txt")); | |
var bounds = new { | |
TopPoint = new Point(monitorLocation.X, monitorLocation.Y - 150), | |
ZeroPoint = new Point(0,0), | |
Size = new Size(600,150), | |
}; | |
var barrierPoint = new { | |
Top = new Point(65,bounds.Size.Height), | |
Bottom = new Point(65, bounds.Size.Height-50) | |
}; | |
var groundPoint = new { | |
Start = new Point(0, bounds.Size.Height - 15), | |
End = new Point(bounds.Size.Width, bounds.Size.Height - 15) | |
}; | |
var topOfDinoPoint = new { | |
Start = new Point(0, bounds.Size.Height - 50), | |
End = new Point(bounds.Size.Width, bounds.Size.Height - 50) | |
}; | |
var centerOfDino = new Point(barrierPoint.Top.X - 15 , barrierPoint.Top.Y + ((barrierPoint.Bottom.Y - barrierPoint.Top.Y)/2) - 17 ); | |
var cactusColor = ColorTranslator.FromHtml("#535353"); | |
var content = new DumpContainer(); | |
var stats = new { DinoCam = content, CURRENT_STATUS = new DumpContainer(), DistanceToThreat = new DumpContainer() }.Dump(); // , ThreatWidth = new DumpContainer()}.Dump(); | |
var p = new Pen(Color.Green, 1f); | |
var redP = new Pen(Color.Red) { DashPattern = new [] { 2.0f, 2.0f } }; | |
var alphaRedP = new SolidBrush(Color.FromArgb(150, Color.Red)); | |
var distanceToSearch = 400 ; | |
var minY = topOfDinoPoint.Start.Y; | |
var maxY = groundPoint.Start.Y; | |
Thread.CurrentThread.Priority = ThreadPriority.Highest; | |
Bitmap b = null; | |
var needToJump = false; var actionString = "CHILLING"; var distToThreatString = "ALL CLEAR BABY"; var threatWidthString = ""; | |
var threatWidth = 0; | |
var threatDistance = 0; | |
Action<Graphics> updateDinoCam = g => | |
{ | |
stats.CURRENT_STATUS.Content = needToJump ? "JUMPING" : "CHILLING"; | |
stats.DistanceToThreat.Content = threatDistance == 0 ? distToThreatString : threatDistance.ToString(); | |
g.DrawLine(p, barrierPoint.Top, barrierPoint.Bottom ); // draw dino collision point | |
g.DrawLine(p, groundPoint.Start, groundPoint.End); // draw ground bound | |
g.DrawLine(p, topOfDinoPoint.Start, topOfDinoPoint.End); // draw dino head bound | |
content.Content = b; | |
}; | |
while (true) | |
{ | |
b = new Bitmap(bounds.Size.Width, bounds.Size.Height); | |
using (var g = Graphics.FromImage(b)) | |
{ | |
g.CopyFromScreen(bounds.TopPoint, bounds.ZeroPoint, bounds.Size); // snap screen | |
// find the nearest cactus | |
var threat = Enumerable.Range(barrierPoint.Top.X, distanceToSearch) | |
.SelectMany(j=> Enumerable.Range(minY, maxY - minY).Select(i=> new { X = j, Y = i })) | |
.FirstOrDefault(pt => b.GetPixel(pt.X,pt.Y) == cactusColor); | |
if (threat == null) { updateDinoCam(g); continue; } // no cactus, nothing to do.. | |
// find end of cactus | |
var threatPt = new Point(threat.X, threat.Y); | |
var lastCactusPixel = | |
Enumerable.Range(threat.X, 100) | |
.Where(x => x > 0 && x < bounds.Size.Width) | |
.Select(x => new { Point = new Point(x, threat.Y), IsCactus = b.GetPixel(x, threat.Y) == cactusColor }) | |
.FirstWithLookhead(10, (pix, nextPixels) => pix.IsCactus && nextPixels.All(ppx=> !ppx.IsCactus)); | |
threatWidth = lastCactusPixel.Point.X - threatPt.X; | |
threatDistance = threat.X - barrierPoint.Top.X; | |
// highlight threat on dinocam | |
g.DrawLine(redP, centerOfDino, threatPt); | |
g.DrawLine(redP, centerOfDino, new Point(lastCactusPixel.Point.X, lastCactusPixel.Point.Y)); | |
g.FillRectangle(alphaRedP, new Rectangle(new Point(threatPt.X, threatPt.Y -5) , new Size(threatWidth, 10))); | |
// if we need to jump, Lets Do That | |
needToJump = IsThisTooCloseForComfort(threatWidth, threatDistance); | |
if (needToJump) | |
Keyboard.HoldKey((byte)Keys.Space, 300); // JUMP!!!! | |
updateDinoCam(g); | |
} | |
} | |
} | |
public bool IsThisTooCloseForComfort (int threatWidth, int distance) | |
{ | |
// small cactus | |
if (threatWidth < 25 && (distance < 115)) | |
return true; | |
else if (threatWidth < 40 && (distance < 105)) | |
return true; | |
// medium cactus | |
else if (threatWidth < 55 && (distance < 95)) | |
return true; | |
// large cactus | |
else if (threatWidth < 100 && (distance < 75)) | |
return true; | |
return false; | |
} | |
// Define other methods and classes here | |
public static class Ext | |
{ | |
public static T FirstWithLookhead<T>(this IEnumerable<T> items, int lookAhead, Func<T, List<T>, bool> predicate) | |
{ | |
var ahead = new List<T>(); | |
foreach (var item in items) | |
{ | |
ahead.Add(item); | |
if (ahead.Count > lookAhead) | |
{ | |
var theItem = ahead[0]; | |
ahead.Remove(theItem); | |
var result = predicate(theItem, ahead); | |
if (result) | |
return theItem; | |
} | |
} | |
while (ahead.Count > 0) | |
{ | |
var theItem = ahead[0]; | |
ahead.Remove(theItem); | |
var result = predicate(theItem, ahead); | |
if (result) | |
return theItem; | |
} | |
return default(T); | |
} | |
public static T Do<T>(this T obj, Action<T> action) | |
{ | |
action(obj); | |
return obj; | |
} | |
public static Color Dump(this Color colour, string caption = null) | |
{ | |
Util.RawHtml(String.Format("<div style='text-align:center;display:block;width:20px;height:20px;background-color:#ff6600'>hi</div>")) | |
.Dump(caption); | |
return colour; | |
} | |
public static object DivForColor(this Color colour) | |
{ | |
return Util.RawHtml(String.Format("<div style='text-align:center;display:block;width:20px;height:20px;background-color:{0}'></div>", | |
ColorTranslator.ToHtml(colour))); | |
} | |
static Bitmap screenPixel = new Bitmap(1,1, PixelFormat.Format32bppArgb); | |
public static Color GetColorAt(Point location) | |
{ | |
using (Graphics gdest = Graphics.FromImage(screenPixel)) | |
{ | |
using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero)) | |
{ | |
IntPtr hSrcDC = gsrc.GetHdc(); | |
IntPtr hDC = gdest.GetHdc(); | |
int retval = UserQuery.BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy); | |
gdest.ReleaseHdc(); | |
gsrc.ReleaseHdc(); | |
} | |
} | |
return screenPixel.GetPixel(0, 0); | |
} | |
public static bool IsColorWithinRange(Color c, Point p, int range) | |
{ | |
var xs = Enumerable.Range(0,range).Select(x=> p.X + x); | |
var ys = Enumerable.Range(range/2,(int)(range/1.5) ).Select(y=> p.Y + y); | |
return | |
xs.SelectMany(x=> ys.Select(y=> new Point(x,y))) | |
.Any(pt=> GetColorAt(pt) == c); | |
} | |
} | |
public class Keyboard | |
{ | |
[DllImport("user32.dll", SetLastError = true)] | |
static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo); | |
const int PauseBetweenStrokes = 50; | |
const int KEY_DOWN_EVENT = 0x0001; //Key down flag | |
const int KEY_UP_EVENT = 0x0002; //Key up flag | |
public static void HoldKey(byte key, int duration) | |
{ | |
keybd_event(key, 0, KEY_DOWN_EVENT, 0); | |
Task.Delay(duration).ContinueWith(_=> keybd_event(key, 0, KEY_UP_EVENT, 0)); | |
Task.Delay(50).Wait(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is SOOOO cool!