//=========================================================================== // SNAKE w/ a head and arrow keys //=========================================================================== // revised 2006... // Arrow Keys have been added to direct the snake. // The snake has four heads, one for each direction. // 1) Set Form1->KeyPreview to true; // 2) Create the Form1->OnKeyDown handler... // 3) In that handler include instructions for the VK_LEFT and other keys // // You will need to make sure that the buttons are not IN FOCUS in order // that the arrow keys will be recognized. To do this, make sure that // after any button is pressed, FOCUS is returned to the Edit box. // // 4) Include an "EditLength->SetFocus();" statement at the end of each // Button event handler. //=========================================================================== #include #pragma hdrstopFlag #include "Unit1.h" #include #include "math.h" // Needed to make randomize() work #include "time.h" // Needed to make randomize() work #define UP 0 #define RIGHT 1 #define DOWN 2 #define LEFT 3 #define EMPTY 0 #define SNAKE 1 #define FOOD 2 #define POISON 3 //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { DoubleBuffered = true; } // ========================================================================== // VARIABLES // ========================================================================== bool ateFood = false; // the world int world[25][25]; // these take care of the toroidal world int east, south, relativeE, relativeS, wrappedE, wrappedS; // the positions of each segment in the snake int snakeE[650], snakeS[650]; int snakeHeadE, snakeHeadS; int snakeTailE, snakeTailS; int snakeLength; int segment; int direction, lastDirection, reverseDirection; int span; int maxSize = 25; int iterations = 0; // ========================================================================== // FUNCTIONS // ========================================================================== //------------------------------------------------------------------- Render void render(void) { // essentially, you need to remove the images from the board or they // will cover up the rectangles Form1->ImageHeadUp->Visible = false; // add this Form1->ImageHeadRight->Visible = false; // add this Form1->ImageHeadDown->Visible = false; // add this Form1->ImageHeadLeft->Visible = false; // add this /* Form1->ImageHeadUp->Left = 600; // this also works Form1->ImageHeadUp->Top = 192; // but you don't Form1->ImageHeadRight->Left = 624; // need both Form1->ImageHeadRight->Top = 216; // if you use it you Form1->ImageHeadDown->Left = 600; // can remove all the Form1->ImageHeadDown->Top = 240; // visible = true and Form1->ImageHeadLeft->Left = 576; // visible = false Form1->ImageHeadLeft->Top = 216; // statements. */ Application->ProcessMessages(); // add this // This is the old code which redraws the entire world // It is really inefficient, since it redraws all the squares // but it works... for (east=0; eastCanvas->Pen->Color=clRed; Form1->Canvas->Brush->Color=clRed; Form1->Canvas->Rectangle (east*20,south*20,east*20+20,south*20+20); break; case SNAKE: // snake Form1->Canvas->Pen->Color=clLime; Form1->Canvas->Brush->Color=clLime; Form1->Canvas->Rectangle (east*20,south*20,east*20+20,south*20+20); break; case FOOD: // food Form1->Canvas->Pen->Color=clBlue; Form1->Canvas->Brush->Color=clBlue; Form1->Canvas->Rectangle (east*20,south*20,east*20+20,south*20+20); break; case POISON: // poison Form1->Canvas->Pen->Color=clFuchsia; Form1->Canvas->Brush->Color=clFuchsia; Form1->Canvas->Rectangle (east*20,south*20,east*20+20,south*20+20); break; } } } // This is a little fancy, choosing a head to suit the direction switch (direction) { // add this case UP: { // add this Form1->ImageHeadUp->Top = snakeHeadS * 20; // add this Form1->ImageHeadUp->Left = snakeHeadE * 20; // add this Form1->ImageHeadUp->Visible = true; // add this Form1->ImageHeadUp->Repaint(); // add this break; // add this } // add this case RIGHT: { // add this Form1->ImageHeadRight->Top = snakeHeadS * 20; // add this Form1->ImageHeadRight->Left = snakeHeadE * 20; // add this Form1->ImageHeadRight->Visible = true; // add this Form1->ImageHeadRight->Repaint(); // add this break; // add this } // add this case DOWN: { // add this Form1->ImageHeadDown->Top = snakeHeadS * 20; // add this Form1->ImageHeadDown->Left = snakeHeadE * 20; // add this Form1->ImageHeadDown->Visible = true; // add this Form1->ImageHeadDown->Repaint(); // add this break; // add this } // add this case LEFT: { // add this Form1->ImageHeadLeft->Top = snakeHeadS * 20; // add this Form1->ImageHeadLeft->Left = snakeHeadE * 20; // add this Form1->ImageHeadLeft->Visible = true; // add this Form1->ImageHeadLeft->Repaint(); // add this break; // add this } // add this } // add this } //---------------------------------------------------------------- Game Over void gameOver (void) { Form1->Timer1->Enabled = false; Form1->Color = clBlack; render(); Application->ProcessMessages(); } //----------------------------------------------------------------- Clear All void clearAll (void) { for (east=0; eastTimer1->Enabled = true; } //---------------------------------------------------------------------- STEP void step (void) { // cannot fold back on yourself reverseDirection = direction + 2; if (reverseDirection > 3) reverseDirection = reverseDirection - 4; if (lastDirection == reverseDirection) direction = lastDirection; // figure out where new head will be switch (direction) { case UP: // up snakeHeadS--; if (snakeHeadS < 0) snakeHeadS = 24; break; case RIGHT: // right snakeHeadE++; if (snakeHeadE > 24) snakeHeadE = 0; break; case DOWN: // down snakeHeadS++; if (snakeHeadS > 24) snakeHeadS = 0; break; case LEFT: // left snakeHeadE--; if (snakeHeadE <0) snakeHeadE = 24; break; } // if you eat poison or eat yourself then the game is over if (world[snakeHeadE][snakeHeadS] == 3) gameOver(); if (world[snakeHeadE][snakeHeadS] == 1) gameOver(); // if there is food there remember to add to length if (world[snakeHeadE][snakeHeadS] == 2) ateFood = true; world[snakeHeadE][snakeHeadS] = SNAKE; // move each segment's E and S position down the snake length for (segment = snakeLength + 1; segment > 0; segment --) { snakeE[segment] = snakeE[segment - 1]; snakeS[segment] = snakeS[segment - 1]; } // and the snake head at the front snakeE[segment] = snakeHeadE; snakeS[segment] = snakeHeadS; // remove the snake's last tail position if it hasn't eaten if (!ateFood) { snakeTailE = snakeE[snakeLength ]; snakeTailS = snakeS[snakeLength ]; } if (ateFood) { snakeLength++; ateFood = false; } lastDirection = direction; // snake cannot double back on self world[snakeTailE][snakeTailS] = false; render(); // show some statistics Form1->EditLength->Text = snakeLength; } //-------------------------------------------------------------------- Begin void begin (void) { clearAll(); makeRandomFood(); makeRandomPoison(); makeRandomSnake(); render(); } // ========================================================================== // EVENT HANDLERS // ========================================================================== //----------------------------------------------------------------- Form Show void __fastcall TForm1::FormShow(TObject *Sender) { randomize(); } //-------------------------------------------------------------- Begin Button void __fastcall TForm1::ButtonBeginClick(TObject *Sender) { begin(); EditLength->SetFocus(); // ensures the arrow keys will be recognized } //-------------------------------------------------------------- Pause Button void __fastcall TForm1::ButtonPauseMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { Timer1->Enabled = !Timer1->Enabled; if (Timer1->Enabled == true) { ButtonPause->Caption = "Pause"; PanelPause->Color = clRed; } else { ButtonPause->Caption = "Resume"; PanelPause->Color = clLime; } EditLength->SetFocus(); // ensures the arrow keys will be recognized } //--------------------------------------------------------------- Step Button void __fastcall TForm1::ButtonStepClick(TObject *Sender) { step(); EditLength->SetFocus(); // ensures the arrow keys will be recognized } //------------------------------------------------------------------ On Timer void __fastcall TForm1::Timer1Timer(TObject *Sender) { step(); Application->ProcessMessages(); } //------------------------------------------------------------- Form Key Down void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { if (Key == VK_LEFT) direction = 3; if (Key == VK_RIGHT) direction = 1; if (Key == VK_UP) direction = 0; if (Key == VK_DOWN) direction = 2; if (Key == VK_NUMPAD0) begin(); } // ========================================================================== // THAT'S ALL FOLKS // ==========================================================================