// // Kiwi Scientific Acceleration // Interactive Game // (C) 2009 DJ Greaves - University of Cambridge, Computer Laboratory // using System; using KiwiSystem; // Simple random generator - should load this from library in future. class prbs { static int prbv = 2; public static void reseed(int q) { prbv = q; } public static int randy() { int nv = 1 & ((prbv >> 14) ^ (prbv >> 13)); prbv = (prbv == 0) ? 1234 : ((prbv << 1) + nv) & 0x7FFF; return prbv; } } // A graphical plot of the score as a sort of progress indicator. public class scoreBar { void plotScoreBar_lower(int vv, bool unplot=false) { // 182 = nice gold colour uint xpos = (uint)vv; FramestoreDriver.DrawBlock(xpos, 3, 4, 13, (byte)((unplot)?255:182)); } int oldPlottedScore = 44; public void plotScoreBarTop(int score) { int vv = (score / (SnakeGame.maxScoreLimit / FramestoreDriver.pix_width)); if (vv != oldPlottedScore) { plotScoreBar_lower(oldPlottedScore, true); oldPlottedScore = vv; plotScoreBar_lower(oldPlottedScore, false); } } } class SnakeGame { [Kiwi.InputBitPort("harder_mode")] static bool harder_mode; const int boxfact = 16; const int maxBodyLen = 1024; public const int maxScoreLimit = 256*4; const int lenIncreasePeriod = 7; const int movePeriod_easy = 20; const int movePeriod_hard = 12; static scoreBar scorebarInstance; // Obstacles could have +ve or -ve credit. Here we have a bool map of where they are located and all have -ve credit, so must be avoided. static bool [,] Obstacles = new bool [FramestoreDriver.pix_height/boxfact, FramestoreDriver.pix_width/boxfact]; static void clearObstacles() { for (int y=0;y<FramestoreDriver.pix_height/boxfact; y++) for (int x=0; x<FramestoreDriver.pix_width/boxfact; x++) { Obstacles[y,x] = false; } } // A 2-D point struct point { public short xx; public short yy; } static point [] snakeBody = new point [maxBodyLen]; // Co-ordinates of the snake body elements. static int snakeLen = 0; // Length of the snake static int snakeHead = 0; // Which array index is the head (avoids shifting it up). static int score = 0; static void plotHead(bool unplot=false) { point pp = snakeBody[snakeHead]; // 45=yellow not nice one white. 3=blue. violet=130 nice FramestoreDriver.DrawSqBlock((uint)(pp.xx+1), (uint)(pp.yy+1), 13, (byte)((unplot)?255:130)); // FramestoreDriver.DrawSquare((uint)(pp.xx+3), (uint)(pp.yy+3), 3, (byte)((unplot)?255:33)); } static void renderSceneryBlock(int x0, int y0) { uint x1 = (uint)(x0 * boxfact); uint y1 = (uint)(y0 * boxfact); // colour 11 = ? FramestoreDriver.DrawSquare(x1+4, y1+4, 3, 11); FramestoreDriver.DrawSquare(x1+2, y1+2, 7, 11); FramestoreDriver.DrawSquare(x1, y1, 11, 11); Obstacles[y0, x0] = true; } static void genSceneryBlock() { int x0 = 3; int y0 = 3; int len = 6; bool dir = true; for (int p=0; p<len; p++) { renderSceneryBlock(x0+(dir?p:0), y0+(dir?0:p)); x0 = (3*(prbs.randy() & (FramestoreDriver.pix_width-1)))/4; y0 = (3*(prbs.randy() & (FramestoreDriver.pix_height-1)))/4; dir = (prbs.randy() & 4) == 4; len = (prbs.randy() & 3)+1; } } static ushort decimal1000(ushort arg) { ushort d0 = (ushort)(arg / (ushort)1000); arg -= (ushort)(d0 * (ushort)1000); int d1 = (ushort)(arg / (ushort)100); arg -= (ushort)(d1 * (ushort)100); int d2 = (ushort)(arg / (ushort)10); arg -= (ushort)(d2 * (ushort)10); ushort d3 = arg; return (ushort)((d0<<12) + (d1<<8) + (d2<<4) + (d3 << 0)); } static int mc = 0; static int lc = 0; static int dir = 0; static bool reset = true; const bool scene_on = true; public static void RunMe() { if (reset) { reset = false; int headX = FramestoreDriver.pix_width/2, headY=FramestoreDriver.pix_height/2; point pp; pp.xx = (short)headX; pp.yy = (short)headY; snakeHead = 0; snakeBody[snakeHead] = pp; snakeLen = 1; plotHead(); clearObstacles(); ulong seedin = DuplexInterFPGALink.Read64(); prbs.reseed((int)(seedin >> 32) ^ (int)seedin); if (scene_on) { for (int qq=0; qq<5; qq++) genSceneryBlock(); } } mc = mc + 1; int movePeriod = (harder_mode) ? movePeriod_hard: movePeriod_easy; if (mc == movePeriod) { if (score < maxScoreLimit) score = score + 1; GameAppTop.mon32 = decimal1000((ushort)score); mc = 0; point pp = snakeBody[snakeHead]; snakeHead = (snakeHead == (snakeLen-1))? 0:snakeHead+1; point uplot = snakeBody[snakeHead]; Obstacles[uplot.yy/boxfact, uplot.xx/boxfact] = false; plotHead(true); // Unplot old location. int headX = pp.xx; int headY = pp.yy; ulong wordin = DuplexInterFPGALink.Read64(); Console.WriteLine("Word in {0}", wordin); short x_vel = (short)(wordin >> 48); short y_vel = (short)(wordin >> 32); int pushes = (int)(wordin >> 14) & 3; GameAppTop.unary_leds = pushes; // Further Debug monitoring to ARM GameAppTop.mon3 = (int)wordin; // Further Debug monitoring to ARM if (false) GameAppTop.mon32 = (int)(wordin >> 32); // Restrict controls so cannot turn 180 degrees back on self. if (y_vel <= -32 /* && dir != 1 */) dir = 3; else if (y_vel >= 32 /* && dir != 3 */) dir = 1; else if (x_vel >= 32 /* && dir != 0 */) dir = 2; else if (x_vel <= -32 /* && dir != 2 */) dir = 0; int newheadX = headX; int newheadY = headY; if (dir == 0) newheadX = headX + boxfact; else if (dir == 1) newheadY = headY - boxfact; else if (dir == 2) newheadX = headX - boxfact; else newheadY = headY + boxfact; if (true) // Clip to boarders. { if (newheadX < 0) { newheadX = 0; dir = (dir + 1)%4; } if (newheadY < 0) { headY = 0; dir = (dir + 1)%4; } if (newheadX >= FramestoreDriver.pix_width) { newheadX = headX; dir = (dir + 1)%4; } if (newheadY >= FramestoreDriver.pix_height) { newheadY = headY; dir = (dir + 1)%4; } } headX = newheadX; headY = newheadY; bool collision = Obstacles[headY/boxfact, headX/boxfact]; Obstacles[headY/boxfact, headX/boxfact] = true; const int penalty = 20; if (false) scorebarInstance.plotScoreBarTop(score); if (collision) { if (score > penalty) score -= penalty; else score = 0; } pp.xx = (short)headX; pp.yy = (short)headY; snakeBody[snakeHead] = pp; plotHead(); // Consider length increase lc = lc + 1; if (lc == lenIncreasePeriod && snakeLen != maxBodyLen-1) { lc = 0; snakeLen = snakeLen+1; } } } } class GameAppTop { [Kiwi.OutputWordPort("mon32")] public static int mon32; // Debug monitoring to screen [Kiwi.OutputWordPort("mon0")] static int mon0; // Further Debug monitoring to ARM [Kiwi.OutputWordPort("mon3")] public static int mon3; // Further Debug monitoring to ARM [Kiwi.OutputWordPort("unary_leds")] public static int unary_leds; // Provided a method to set [Kiwi.InputBitPort("rtc50")] static bool rtc50; // 50 Hz clock or so static bool old_rtc50 = false; public static void timer_delay(int nn = 1) { while (nn != 0) { nn -= 1; while (old_rtc50 == rtc50) { Kiwi.Pause(); } old_rtc50 = rtc50; Kiwi.Pause(); } } public static void RunGame() { Kiwi.Pause(); FramestoreDriver.sendCmd(1); timer_delay(5); if (false) { FramestoreDriver.DrawTestDiagonal(); Console.WriteLine("Diag Drawn"); Kiwi.KppMark("DIAG DONE"); FramestoreDriver.DrawSquare(4, 4, 20); Kiwi.KppMark("SQ DONE"); } mon32 = 100; FramestoreDriver.DrawBlock(0, 0, FramestoreDriver.pix_width, FramestoreDriver.pix_height, (byte)255 /*white*/); // Blank screen Console.WriteLine("Sq2"); Kiwi.KppMark("SQ2"); short pos_x = FramestoreDriver.pix_width/2, pos_y = FramestoreDriver.pix_height/2; int cursor_col = 0; bool npush, oldpush = false; Kiwi.KppMark("MAINLOOP"); while(true) { /* npush = read_stick_button(0); if (npush && !oldpush) { if (false) adapt_centroids(kmd); } oldpush = npush; if (recal) { calibrate_origins(); continue; } */ timer_delay(); SnakeGame.RunMe(); if (false) { ulong wordin = DuplexInterFPGALink.Read64(); cursor_col += 1; pos_x = (short)(wordin >> 16); // Correct joystick sign and graphical +ve y being down. pos_y = (short)(wordin); FramestoreDriver.framestore_draw_crosshair_cursor((ushort)(pos_x), (ushort)(pos_y), (uint)cursor_col); Kiwi.Pause(); } } return; } } public static class SnakeGameBench { [Kiwi.OutputBitPort("done")] static bool done; public static void Main() { RunHW(); } [Kiwi.HardwareEntryPoint()] public static void RunHW() { Console.WriteLine("Kiwi Snake Game --- Start"); Kiwi.KppMark("START"); GameAppTop.RunGame(); Kiwi.KppMark("FINISH"); Console.WriteLine("Demo Finish"); done = true; } } // eof