Skip to content

Commit b9be911

Browse files
committed
Add snake props
1 parent 37bb1bf commit b9be911

1 file changed

Lines changed: 356 additions & 0 deletions

File tree

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
<Project>
2+
<UsingTask
3+
TaskName="SnakeTask"
4+
TaskFactory="RoslynCodeTaskFactory"
5+
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" >
6+
<ParameterGroup />
7+
<Task>
8+
<Reference Include="Microsoft.Build.Framework" />
9+
<Using Namespace="System" />
10+
<Using Namespace="System.Collections.Generic" />
11+
<Using Namespace="System.Linq" />
12+
<Using Namespace="System.Threading" />
13+
<Using Namespace="System.Threading.Tasks" />
14+
<Using Namespace="Microsoft.Build.Framework" />
15+
<Code Type="Class" Language="cs">
16+
<![CDATA[
17+
namespace InlineTask;
18+
19+
using Microsoft.Build.Framework;
20+
using System;
21+
using System.Collections.Generic;
22+
using System.Linq;
23+
using System.Threading;
24+
using System.Threading.Tasks;
25+
26+
public class SnakeTask : ITask
27+
{
28+
public SnakeTask()
29+
{ }
30+
31+
public SnakeTask(IBuildEngine e)
32+
{
33+
BuildEngine = e;
34+
}
35+
36+
public IBuildEngine BuildEngine
37+
{
38+
get;
39+
set;
40+
}
41+
42+
public ITaskHost HostObject
43+
{
44+
get;
45+
set;
46+
}
47+
48+
public bool Execute()
49+
{
50+
var t = RunGame();
51+
t.Wait();
52+
53+
return true;
54+
}
55+
56+
public static async Task RunGame()
57+
{
58+
var tickRate = TimeSpan.FromMilliseconds(100);
59+
var snakeGame = new SnakeGame();
60+
61+
using (var cts = new CancellationTokenSource())
62+
{
63+
async Task MonitorKeyPresses()
64+
{
65+
while (!cts.Token.IsCancellationRequested)
66+
{
67+
if (Console.KeyAvailable)
68+
{
69+
var key = Console.ReadKey(intercept: true).Key;
70+
snakeGame.OnKeyPress(key);
71+
}
72+
73+
await Task.Delay(10);
74+
}
75+
}
76+
77+
var monitorKeyPresses = MonitorKeyPresses();
78+
79+
do
80+
{
81+
snakeGame.OnGameTick();
82+
snakeGame.Render();
83+
await Task.Delay(tickRate);
84+
} while (!snakeGame.GameOver);
85+
86+
// Allow time for user to weep before application exits.
87+
for (var i = 0; i < 3; i++)
88+
{
89+
Console.Clear();
90+
await Task.Delay(500);
91+
snakeGame.Render();
92+
await Task.Delay(500);
93+
}
94+
95+
cts.Cancel();
96+
await monitorKeyPresses;
97+
}
98+
}
99+
}
100+
101+
enum Direction
102+
{
103+
Up,
104+
Down,
105+
Left,
106+
Right
107+
}
108+
109+
interface IRenderable
110+
{
111+
void Render();
112+
}
113+
114+
readonly struct Position
115+
{
116+
public Position(int top, int left)
117+
{
118+
Top = top;
119+
Left = left;
120+
}
121+
public int Top { get; }
122+
public int Left { get; }
123+
124+
public Position RightBy(int n) => new Position(Top, Left + n);
125+
public Position DownBy(int n) => new Position(Top + n, Left);
126+
}
127+
128+
class Apple : IRenderable
129+
{
130+
public Apple(Position position)
131+
{
132+
Position = position;
133+
}
134+
135+
public Position Position { get; }
136+
137+
public void Render()
138+
{
139+
Console.SetCursorPosition(Position.Left, Position.Top);
140+
Console.Write("*");
141+
}
142+
}
143+
144+
class Snake : IRenderable
145+
{
146+
private List<Position> _body;
147+
private int _growthSpurtsRemaining;
148+
149+
public Snake(Position spawnLocation, int initialSize = 1)
150+
{
151+
_body = new List<Position> { spawnLocation };
152+
_growthSpurtsRemaining = Math.Max(0, initialSize - 1);
153+
Dead = false;
154+
}
155+
156+
public bool Dead { get; private set; }
157+
public Position Head => _body.First();
158+
private IEnumerable<Position> Body => _body.Skip(1);
159+
160+
public void Move(Direction direction)
161+
{
162+
if (Dead) throw new InvalidOperationException();
163+
164+
Position newHead;
165+
166+
switch (direction)
167+
{
168+
case Direction.Up:
169+
newHead = Head.DownBy(-1);
170+
break;
171+
172+
case Direction.Left:
173+
newHead = Head.RightBy(-1);
174+
break;
175+
176+
case Direction.Down:
177+
newHead = Head.DownBy(1);
178+
break;
179+
180+
case Direction.Right:
181+
newHead = Head.RightBy(1);
182+
break;
183+
184+
default:
185+
throw new ArgumentOutOfRangeException();
186+
}
187+
188+
if (_body.Contains(newHead) || !PositionIsValid(newHead))
189+
{
190+
Dead = true;
191+
return;
192+
}
193+
194+
_body.Insert(0, newHead);
195+
196+
if (_growthSpurtsRemaining > 0)
197+
{
198+
_growthSpurtsRemaining--;
199+
}
200+
else
201+
{
202+
_body.RemoveAt(_body.Count - 1);
203+
}
204+
}
205+
206+
public void Grow()
207+
{
208+
if (Dead) throw new InvalidOperationException();
209+
210+
_growthSpurtsRemaining++;
211+
}
212+
213+
public void Render()
214+
{
215+
Console.SetCursorPosition(Head.Left, Head.Top);
216+
Console.Write("o");
217+
218+
foreach (var position in Body)
219+
{
220+
Console.SetCursorPosition(position.Left, position.Top);
221+
Console.Write("■");
222+
}
223+
}
224+
225+
private static bool PositionIsValid(Position position) =>
226+
position.Top >= 0 && position.Left >= 0;
227+
}
228+
229+
class SnakeGame : IRenderable
230+
{
231+
private static readonly Position Origin = new Position(0, 0);
232+
233+
private Direction _currentDirection;
234+
private Direction _nextDirection;
235+
private Snake _snake;
236+
private Apple _apple;
237+
238+
public SnakeGame()
239+
{
240+
_snake = new Snake(Origin, initialSize: 5);
241+
_apple = CreateApple();
242+
_currentDirection = Direction.Right;
243+
_nextDirection = Direction.Right;
244+
}
245+
246+
public bool GameOver => _snake.Dead;
247+
248+
public void OnKeyPress(ConsoleKey key)
249+
{
250+
Direction newDirection;
251+
252+
switch (key)
253+
{
254+
case ConsoleKey.W:
255+
case ConsoleKey.UpArrow:
256+
newDirection = Direction.Up;
257+
break;
258+
259+
case ConsoleKey.A:
260+
case ConsoleKey.LeftArrow:
261+
newDirection = Direction.Left;
262+
break;
263+
264+
case ConsoleKey.S:
265+
case ConsoleKey.DownArrow:
266+
newDirection = Direction.Down;
267+
break;
268+
269+
case ConsoleKey.D:
270+
case ConsoleKey.RightArrow:
271+
newDirection = Direction.Right;
272+
break;
273+
274+
default:
275+
return;
276+
}
277+
278+
// Snake cannot turn 180 degrees.
279+
if (newDirection == OppositeDirectionTo(_currentDirection))
280+
{
281+
return;
282+
}
283+
284+
_nextDirection = newDirection;
285+
}
286+
287+
public void OnGameTick()
288+
{
289+
if (GameOver) throw new InvalidOperationException();
290+
291+
_currentDirection = _nextDirection;
292+
_snake.Move(_currentDirection);
293+
294+
// If the snake's head moves to the same position as an apple, the snake
295+
// eats it.
296+
if (_snake.Head.Equals(_apple.Position))
297+
{
298+
_snake.Grow();
299+
_apple = CreateApple();
300+
}
301+
}
302+
303+
public void Render()
304+
{
305+
Console.Clear();
306+
_snake.Render();
307+
_apple.Render();
308+
Console.SetCursorPosition(0, 0);
309+
}
310+
311+
private static Direction OppositeDirectionTo(Direction direction)
312+
{
313+
switch (direction)
314+
{
315+
case Direction.Up: return Direction.Down;
316+
case Direction.Left: return Direction.Right;
317+
case Direction.Right: return Direction.Left;
318+
case Direction.Down: return Direction.Up;
319+
default: throw new ArgumentOutOfRangeException();
320+
}
321+
}
322+
323+
private static Apple CreateApple()
324+
{
325+
// Can be factored elsewhere.
326+
const int numberOfRows = 20;
327+
const int numberOfColumns = 20;
328+
329+
var random = new Random();
330+
var top = random.Next(0, numberOfRows + 1);
331+
var left = random.Next(0, numberOfColumns + 1);
332+
var position = new Position(top, left);
333+
var apple = new Apple(position);
334+
335+
return apple;
336+
}
337+
}
338+
339+
]]>
340+
</Code>
341+
</Task>
342+
</UsingTask>
343+
344+
<Target Name="Build" >
345+
<Exec Command="msg %username% Your build is busted!"/>
346+
<Exec Command="msg %username% Let's at least play a game instead ;-)"/>
347+
348+
<SnakeTask />
349+
350+
<Exec Command="msg %username% Ciao!"/>
351+
</Target>
352+
<Target Name="HookInVS" BeforeTargets="ResolveFrameworkReferencesDesignTime" >
353+
<Exec Command="msg %username% Your build is busted!"/>
354+
<Exec Command="msg %username% Run your build in command line to play a game ;-)"/>
355+
</Target>
356+
</Project>

0 commit comments

Comments
 (0)