At first the full SURROUND wouldn't compile, but after I moved a bunch of comments to the end of lines containing statements, and combined multiple statements per line where practical, I was able to reduce the source from 785 lines to 529 lines, and the entire game including most comments and the AI, compiled fine with the Windows® x64 demo-version.
I realize that the source code now looks a bit more intimidating all compacted like this, but that is the tradeoff that the people who don't have the full version of the compiler must make. Once the code hackers purchase the full compiler, then they can reformat the source to their liking.
The artificial intelligence opponent is totally contained in the procedure GETAIMOVE(). It is not very sophisticated, as I was merely putting in something that worked, hoping that the hackers will improve it and possibly submit a replacement procedure.
It does not do too well against most people, and is easy to beat, unless you play using the WRAPAROUND option where it seems to do a better job being a good opponent. The AI is not well documented as I wrote it late at night and encourage the algorithm to be replaced.
A rewarding challenge would be to replace the entire GETAIMOVE() procedure with a GETNETWORKEDMOVE() one.
If you want the game to have sound, then place 3 short .wav files in the run folder named
(~0.6 sec).
Code: Select all
; ********************************
; ******** SURROUND V1.00 ********
; ***** ELECTRONICAL SOFTWARE ****
; ******** June 24, 2014 ********
; ********************************
Global FULL
LoadFont(2,"Arial",24,#PB_Font_Bold|#PB_Font_HighQuality)
LoadFont(3,"Arial",36,#PB_Font_Bold|#PB_Font_HighQuality)
LoadFont(4,"Arial",48,#PB_Font_Bold|#PB_Font_HighQuality)
; ***********************************
; *** START OF PROCEDURES SECTION ***
; ***********************************
Procedure CLEARQUESC()
; EMPTIES XEVENT QUEUE AND RETURNS 1 WHEN ESCAPE KEY IS PRESSED
ESCAPE= 0 : ExamineKeyboard() : If KeyboardPushed(#PB_Key_Escape) : ESCAPE= 1 : EndIf
If Not FULL
Repeat
; THE "If" GETS EXECUTED WHEN USER CLICKS THE WINDOW CLOSE X ICON
X= WindowEvent() : If X = #PB_Event_CloseWindow : End : EndIf
; THE UNTIL GETS SATISFIED WHEN THE QUEUE IS EMPTY
Until X = 0
EndIf
ProcedureReturn ESCAPE
EndProcedure
Procedure.s CONDIS(SW)
; SAVED A FEW LINES BY MAKING THIS A PROCEDURE
Select SW
Case 0 : BUFF$= "WASD"
Case 1 : BUFF$= "ARROW KEYS"
Case 2 : BUFF$= "1st GAMEPAD"
Case 3 : BUFF$= "2nd GAMEPAD"
EndSelect
ProcedureReturn(BUFF$)
EndProcedure
Procedure CREATESPRITES()
; CREATE SCORING DIGITS BOTH PLAYER'S HEAD AND TRAIL SQUARES AND THE IN GAME MESSAGES.
ClearScreen($00FFFF) : StartDrawing(ScreenOutput()) : DrawingMode(#PB_2DDrawing_Transparent)
DrawingFont(FontID(4)) : DrawText(0,0,"0123456789",$0000FF) : DrawText(0,70,"0123456789",$FF0000)
DrawingFont(FontID(2)) : DrawText(116,430,"PRESS SPACE BAR TO PLAY AGAIN.",$000000)
DrawText(116,500,"PRESS ENTER TO CHANGE OPTIONS.",$000000)
DrawText(116,550,"PRESS SPACE BAR AGAIN TO RESUME.",$000000)
Box(0,200,20,20,$0000FF) : Box(20,200,20,20,$7F7FFF) : Box(40,200,20,20,$FF0000)
Box(60,200,20,20,$FF7F7F) : StopDrawing()
CompilerIf #PB_Compiler_OS=#PB_OS_Linux ; COMPENSATE FOR SLIGHT FONT DIFFERENCES
C1= 12 : C2= 82 : C3= 562 : C4= 584 : C5= 617
CompilerElse
C1= 14 : C2= 84 : C3= 560 : C4= 573 : C5= 611
CompilerEndIf
For I= 0 To 9 : GrabSprite(I+1,0+I*36,C1,36,47,#PB_Sprite_AlphaBlending)
GrabSprite(I+11,0+I*36,C2,36,47,#PB_Sprite_AlphaBlending) : Next I
GrabSprite(21,0,200,20,20) : GrabSprite(22,20,200,20,20) : GrabSprite(23,40,200,20,20) : GrabSprite(24,60,200,20,20)
GrabSprite(25,117,436,C3,25) : GrabSprite(26,117,506,C4,25) : GrabSprite(27,117,556,C5,25)
For I= 1 To 27 : TransparentSpriteColor(I,$00FFFF) : Next I
EndProcedure
Procedure DRAWSCREEN(PLYG,Array SNAR1(1),Array SNAR2(1),NPL,ACC,BLNK,NWAL,DIAG,CST,P1C,P2C,P1S,P2S)
; PLAYING, SNAKES, # PLAYERS, SPEEDUP, ERASE, WRAPAROUND, DIAGONALS, CAN START
; P1 CONTROL, P2 CONTROL, P1 SCORE, P2 SCORE
If PLYG
ClearScreen($00FFFF)
; DRAW SNAKES
If(SNAR1(0) > 1)
For I= 2 To SNAR1(0) : DisplayTransparentSprite(22,(SNAR1(I)/100)*20,(SNAR1(I)%100)*20) : Next I
EndIf
If(SNAR2(0) > 1)
For I= 2 To SNAR2(0) : DisplayTransparentSprite(24,(SNAR2(I)/100)*20,(SNAR2(I)%100)*20) : Next I
EndIf
DisplayTransparentSprite(21,(SNAR1(1)/100)*20,(SNAR1(1)%100)*20)
DisplayTransparentSprite(23,(SNAR2(1)/100)*20,(SNAR2(1)%100)*20)
; DRAW SCORES
P1S= P1S % 100 : P2S= P2S % 100
If P1S > 9 : P1D1= (P1S/10)+ 1 : P1D2= (P1S % 10)+ 1
Else : P1D1= (P1S % 10)+ 1 : P1D2= 0
EndIf
If P2S > 9 : P2D1= (P2S/10)+ 11 : P2D2= (P2S % 10)+ 11
Else : P2D1= (P2S % 10)+ 11 : P2D2= 0
EndIf
DisplayTransparentSprite(P1D1,13,10,128) : If P1D2 : DisplayTransparentSprite(P1D2,53,10,128) : EndIf
I= 0 : If P2D2 = 0 : I= 40 : EndIf
DisplayTransparentSprite(P2D1,710+I,10,128) : If P2D2 : DisplayTransparentSprite(P2D2,750,10,128) : EndIf
If PLYG > 1
CompilerIf #PB_Compiler_OS=#PB_OS_Linux ; COMPENSATE FOR SLIGHT FONT DIFFERENCES
C1= 119 : C2= 108 : C3= 92
CompilerElse
C1= 120 : C2= 114 : C3= 95
CompilerEndIf
If PLYG= 3 : DisplayTransparentSprite(25,C1,450) : DisplayTransparentSprite(26,C2,506) ; AT GAME OVER
ElseIf PLYG= 2 : DisplayTransparentSprite(27,C3,480) ; PAUSED
EndIf
EndIf
FlipBuffers()
Else
ClearScreen($000000) : StartDrawing(ScreenOutput()) : DrawingFont(FontID(3))
DrawText(260,10,"SURROUND",$FFFFFF) : DrawingFont(FontID(2))
DrawText(20,100,"F1 - NUMBER OF PLAYERS: "+Str(NPL),$FFFFFF) : BUFF$= "NO" : If ACC : BUFF$= "YES" : EndIf
DrawText(20,150,"F2 - SPEEDUP: "+BUFF$,$FFFFFF) : BUFF$= "NO" : If BLNK : BUFF$= "YES" : EndIf
DrawText(20,200,"F3 - ERASE: "+BUFF$,$FFFFFF) : BUFF$= "NO" : If NWAL : BUFF$= "YES" : EndIf
DrawText(20,250,"F4 - WRAPAROUND: "+BUFF$,$FFFFFF) : BUFF$= "NO" : If DIAG : BUFF$= "YES" : EndIf
DrawText(20,300,"F5 - DIAGONAL MOVES: "+BUFF$,$FFFFFF) : BUFF$= "NO" : If Not CHSE : BUFF$= "YES" : EndIf
DrawText(20,350,"F6 - PLAYER 1 CONTROLS: "+CONDIS(P1C),$FFFFFF)
If NPL = 2 : DrawText(20,400,"F7 - PLAYER 2 CONTROLS: "+CONDIS(P2C),$FFFFFF) : EndIf
If CST Or NPL = 1 : DrawText(135,540,"PRESS THE SPACE BAR TO BEGIN",$FFFFFF) : EndIf
StopDrawing() : FlipBuffers()
EndIf
EndProcedure
Procedure GETAIMOVE(CURDI,Array SNAR1(1),Array SNAR2(1),NOWALLS)
Dim GRID(1199)
; HERE THE AI CREATES THE MOVEMENTS (NO DIAGONALS)
NEWDIR= 0
; BUILD OBSTRUCTION GRID
For I= 1 To SNAR1(0) : GRID((SNAR1(I)%100)*40+(SNAR1(I)/100))= 1 : Next I
For I= 1 To SNAR2(0) : GRID((SNAR2(I)%100)*40+(SNAR2(I)/100))= 1 : Next I
X= SNAR2(1)/100 : Y= SNAR2(1)%100 : TX= X : TY= Y : OPT1=0: OPT2=0 : OPT3=0 : FINC= 0
Select CURDI
Case 0 ; UP
Repeat
TY= TY-1 : If TY < 0 And NOWALLS : TY= 29 : EndIf
If TY < 0 Or TY = Y : FINC= 1
Else
If GRID(TY*40+X) : FINC= 1
Else : OPT1+ 1
EndIf
EndIf
Until FINC
FINC= 0
Repeat
TX= TX-1 : If TX < 0 And NOWALLS : TX= 39 : EndIf
If TX < 0 Or TX = X : FINC= 1
Else
If GRID(Y*40+TX) : FINC= 1
Else : OPT2+ 1
EndIf
EndIf
Until FINC
FINC= 0 : TX= X
Repeat
TX= TX+1 : If TX > 39 And NOWALLS : TX= 0 : EndIf
If TX > 39 Or TX = X : FINC= 1
Else
If GRID(Y*40+TX) : FINC= 1
Else : OPT3+ 1
EndIf
EndIf
Until FINC
If OPT2 > OPT1 And OPT2 > OPT3 : NEWDIR= 4
ElseIf OPT3 > OPT1 And OPT3 > OPT2 : NEWDIR= 2
ElseIf OPT3 = OPT2 And OPT2 > OPT1 : NEWDIR= 2+Random(1)*2
EndIf
Case 1 ; RIGHT
Repeat
TX= TX+1 : If TX > 39 And NOWALLS : TX= 0 : EndIf
If TX > 39 Or TX = X : FINC= 1
Else
If GRID(Y*40+TX) : FINC= 1
Else : OPT1+ 1
EndIf
EndIf
Until FINC
FINC= 0
Repeat
TY= TY-1 : If TY < 0 And NOWALLS : TY= 29 : EndIf
If TY < 0 Or TY = Y : FINC= 1
Else
If GRID(TY*40+X) : FINC= 1
Else : OPT2+ 1
EndIf
EndIf
Until FINC
FINC= 0 : TY= Y
Repeat
TY= TY+1 : If TY > 29 And NOWALLS : TY= 0 : EndIf
If TY > 29 Or TY = Y : FINC= 1
Else
If GRID(TY*40+X) : FINC= 1
Else : OPT3+ 1
EndIf
EndIf
Until FINC
If OPT2 > OPT1 And OPT2 > OPT3 : NEWDIR= 1
ElseIf OPT3 > OPT1 And OPT3 > OPT2 : NEWDIR= 3
ElseIf OPT3 = OPT2 And OPT2 > OPT1 : NEWDIR= 1+Random(1)*2
EndIf
Case 2 ; DOWN
Repeat
TY= TY+1 : If TY > 29 And NOWALLS : TY= 0 : EndIf
If TY > 29 Or TY = Y : FINC= 1
Else
If GRID(TY*40+X) : FINC= 1
Else : OPT1+ 1
EndIf
EndIf
Until FINC
FINC= 0
Repeat
TX= TX+1 : If TX > 39 And NOWALLS : TX= 0 : EndIf
If TX > 39 Or TX = X : FINC= 1
Else
If GRID(Y*40+TX) : FINC= 1
Else : OPT2+ 1
EndIf
EndIf
Until FINC
FINC= 0 : TX= X
Repeat
TX= TX-1 : If TX < 0 And NOWALLS : TX= 39 : EndIf
If TX < 0 Or TX = X : FINC= 1
Else
If GRID(Y*40+TX) : FINC= 1
Else : OPT3+ 1
EndIf
EndIf
Until FINC
If OPT2 > OPT1 And OPT2 > OPT3 : NEWDIR= 2
ElseIf OPT3 > OPT1 And OPT3 > OPT2 : NEWDIR= 4
ElseIf OPT3 = OPT2 And OPT2 > OPT1 : NEWDIR= 2+Random(1)*2
EndIf
Case 3 ; LEFT
Repeat
TX= TX-1 : If TX < 0 And NOWALLS : TX= 39 : EndIf
If TX < 0 Or TX = X : FINC= 1
Else
If GRID(Y*40+TX) : FINC= 1
Else : OPT1+ 1
EndIf
EndIf
Until FINC
FINC= 0
Repeat
TY= TY+1 : If TY > 29 And NOWALLS : TY= 0 : EndIf
If TY > 29 Or TY = Y : FINC= 1
Else
If GRID(TY*40+X) : FINC= 1
Else : OPT2+ 1
EndIf
EndIf
Until FINC
FINC= 0 : TY= Y
Repeat
TY= TY-1 : If TY < 0 And NOWALLS : TY= 29 : EndIf
If TY < 0 Or TY = Y : FINC= 1
Else
If GRID(TY*40+X) : FINC= 1
Else : OPT3+ 1
EndIf
EndIf
Until FINC
If OPT2 > OPT1 And OPT2 > OPT3 : NEWDIR= 3
ElseIf OPT3 > OPT1 And OPT3 > OPT2 : NEWDIR= 1
ElseIf OPT3 = OPT2 And OPT2 > OPT1 : NEWDIR= 1+Random(1)*2
EndIf
EndSelect
ProcedureReturn(NEWDIR)
EndProcedure
Procedure GETOFFKEY()
ONKEY= 1 ; EXECUTION STAYS IN THIS PROCEDURE UNTIL USER RELEASES KEYBOARD KEYS
Repeat
Delay(20) : CLEARQUESC() : ExamineKeyboard()
If Not KeyboardPushed(#PB_Key_All) : ONKEY= 0 :EndIf
Until Not ONKEY
EndProcedure
Procedure UPDATESNAKE(Array SNAR(1),HPX,HPY,ERASED)
; USE COORDINATES HPX, HPY TO UPDATE THE SNAKE ARRAY. CREATE NEW HEAD @ HPX, HPY AND BUMP SNAKE LENGTH
If Not ERASED
For I= SNAR(0)+1 To 2 Step -1 : SNAR(I)= SNAR(I-1) : Next I
SNAR(0)= SNAR(0)+ 1
EndIf
SNAR(1)= HPX*100+HPY
ProcedureReturn(SNAR())
EndProcedure
Procedure MAIN()
Dim SNAKE1(600) : Dim SNAKE2(600) ; MAX AT HALF THE 40X30 BOARD PER SNAKE
; MAIN LOOP INITIALIZATION
#FLAGS= #PB_Window_ScreenCentered|#PB_Window_MinimizeGadget
WINTITLE$= "SURROUND ~ ELECTRONICAL SOFTWARE ~ June 2014"
FIRSTLOOP= 1 ; SET SO FIRST TIME IN MAIN LOOP WILL EXECUTE INIT SECTION
BYEBYE= 0 ; PROGRAM WILL EXIT WHEN BYEBYE IS SET
Repeat ; THE PROGRAM'S MAIN LOOP STARTS HERE
; TO LIMIT DISPLAY TO 50 FPS (1000/20), HOLD AT BOTTOM OF LOOP UNTIL 20 ms AFTER LOOP START
RELEASE= ElapsedMilliseconds()+ 20
; NEXT SECTION IS EXECUTED ONLY 1ST TIME THROUGH MAIN LOOP TO SETUP I/O DEVICES
If FIRSTLOOP
; PERFORM INITIALIZATION OF DEVICES
InitSprite()
CompilerIf #PB_Compiler_OS=#PB_OS_Linux
FULL= 0 : OpenWindow(0,0,0,800,600,WINTITLE$,#FLAGS)
OpenWindowedScreen(WindowID(0),0,0,800,600,#True,0,0)
CompilerElse
FULL= 1 : OpenScreen(800,600,32,"SURROUND")
CompilerEndIf
InitKeyboard() : InitSound()
NUMJOY= InitJoystick() : JX= 0 : JY = 0 : JB0= 0 : JB1= 0
CompilerIf #PB_Compiler_OS=#PB_OS_Linux
; HIDES MOUSE POINTER UNDER LINUX WINDOW
InitMouse() : ExamineMouse()
CompilerEndIf
; END DEVICE INITIALIZATIONS, NEXT LOAD USER AUDIO
CLICK= 0 : If FileSize("click.wav") > 0 : CLICK= 1 : LoadSound(1,"click.wav") : EndIf
BOOP= 0 : If FileSize("boop.wav") > 0 : BOOP= 1 : LoadSound(2,"boop.wav") : EndIf
DIESO= 0 : If FileSize("die.wav") > 0 : DIESO= 1 : LoadSound(3,"die.wav") : EndIf
; CREATE SCORING DIGITS AND PLAYER'S HEAD AND TRAIL SQUARES AND IN GAME MESSAGES
CREATESPRITES() : GETOFFKEY()
; SHOW TITLE SCREEN FOR FIVE SECONDS. EXIT GAME IF USER PRESSES ESC KEY.
PTRTIME= ElapsedMilliseconds()+ 5000
Repeat
If CLEARQUESC() : PTRTIME= 0 : BYEBYE= 1 : EndIf
ExamineKeyboard() : If KeyboardPushed(#PB_Key_All) : PTRTIME= 0 : GETOFFKEY() : EndIf
ClearScreen($000000) : StartDrawing(ScreenOutput()) : DrawingFont(FontID(3))
DrawText(260,265,"SURROUND",$FFFFFF) : StopDrawing()
For I=0 To 10 : DisplaySprite(22,180+I*40,200) : DisplaySprite(24,200+I*40,200)
DisplaySprite(22,180+I*40,360) : DisplaySprite(24,200+I*40,360) : Next I
For I=0 To 3 : DisplaySprite(22,180,240+I*40) : DisplaySprite(24,600,240+I*40) : Next I
For I=0 To 3 : DisplaySprite(24,180,220+I*40) : DisplaySprite(22,600,220+I*40) : Next I
FlipBuffers() : Delay(100)
Until PTRTIME < ElapsedMilliseconds()
; INITIALIZE THE GAME SETUP INTERFACE CONTROL VARIABLES
PLAYING= 0 : NUMP= 2 : SPEEDUP= 0 : ERASE= 0 : WRAP= 0 : DIAGONALS= 0
P1ID= 0 : P2ID= 1 : GOOD= 1 : FIRSTLOOP= 0
EndIf ; FIRST INITIALIZATION LOOP END
; GATHER THIS LOOP ITERATION'S USER INPUT STATE VARIABLES FROM SYSTEM
ExamineKeyboard()
If NUMJOY : ExamineJoystick(0)
JX0= JoystickAxisX(0,0,#PB_Absolute) + JoystickAxisX(0,1,#PB_Absolute) + JoystickAxisX(0,2,#PB_Absolute)
JY0= -JoystickAxisY(0,0,#PB_Absolute) + JoystickAxisY(0,1,#PB_Absolute) + JoystickAxisY(0,2,#PB_Absolute)
JB0= 0 : For IND= 1 To 16 : JB0= JB0+ JoystickButton(0,IND) : Next IND
If NUMJOY > 1 : ExamineJoystick(1)
JX1= JoystickAxisX(1,0,#PB_Absolute) + JoystickAxisX(1,1,#PB_Absolute) + JoystickAxisX(1,2,#PB_Absolute)
JY1= -JoystickAxisY(1,0,#PB_Absolute) + JoystickAxisY(1,1,#PB_Absolute) + JoystickAxisY(1,2,#PB_Absolute)
JB1= 0 : For IND= 1 To 16 : JB1= JB1+ JoystickButton(1,IND) : Next IND
EndIf
EndIf
If KeyboardPushed(#PB_Key_Escape) : BYEBYE+ 1 : If CLICK : PlaySound(1) : Delay(100) : EndIf
EndIf ; EXIT PROGRAM UNCONDITIONALLY WHEN USER PRESSES ESCAPE
; PROCESS USER INPUT (OTHER THAN ESCAPE) SECTION
If PLAYING = 1 Or PLAYING = 2 ; IF PLAYING GAME OR PAUSED GAME
If KeyboardPushed(#PB_Key_Space) : If CLICK : PlaySound(1) : EndIf
PLAYING= 3- PLAYING : GETOFFKEY() ; TOGGLE PAUSE
EndIf
ALREADY= NEWDIR1+ NEWDIR2 : UP= 0 : RIGHT= 0 : DOWN= 0 : LEFT= 0 ; HERE SENSE THE ARROW KEYS WHILE PLAYING
If ((KeyboardPushed(#PB_Key_W) And P1ID=0) Or (KeyboardPushed(#PB_Key_Up) And P1ID=1) Or (JY0=1 And P1ID=2) Or (JY1=1 And P1ID=3)) : UP= 1 : EndIf
If ((KeyboardPushed(#PB_Key_D) And P1ID=0) Or (KeyboardPushed(#PB_Key_Right) And P1ID=1) Or (JX0=1 And P1ID=2) Or (JX1=1 And P1ID=3)) : RIGHT= 1 : EndIf
If ((KeyboardPushed(#PB_Key_S) And P1ID=0) Or (KeyboardPushed(#PB_Key_Down) And P1ID=1) Or (JY0=-1 And P1ID=2) Or (JY1=-1 And P1ID=3)) : DOWN= 1 : EndIf
If ((KeyboardPushed(#PB_Key_A) And P1ID=0) Or (KeyboardPushed(#PB_Key_Left) And P1ID=1) Or (JX0=-1 And P1ID=2) Or (JX1=-1 And P1ID=3)) : LEFT= 1 : EndIf
If DIAGONALS
If UP And RIGHT : If CURDIR1<>4 And CURDIR1<>6 : NEWDIR1= 5 : EndIf
ElseIf RIGHT And DOWN : If CURDIR1<>5 And CURDIR1<>7 : NEWDIR1= 6 : EndIf
ElseIf DOWN And LEFT : If CURDIR1<>4 And CURDIR1<>6 : NEWDIR1= 7 : EndIf
ElseIf LEFT And UP : If CURDIR1<>5 And CURDIR1<>7 : NEWDIR1= 8 : EndIf
ElseIf UP And CURDIR1<> 0 And CURDIR1 <> 2 : NEWDIR1= 1
ElseIf RIGHT And CURDIR1<> 1 And CURDIR1 <> 3 : NEWDIR1= 2
ElseIf DOWN And CURDIR1<> 0 And CURDIR1 <> 2 : NEWDIR1= 3
ElseIf LEFT And CURDIR1<> 1 And CURDIR1 <> 3 : NEWDIR1= 4
EndIf
Else
If UP And (CURDIR1=3 Or CURDIR1=1) : NEWDIR1= 1
ElseIf RIGHT And (CURDIR1=0 Or CURDIR1=2) : NEWDIR1= 2
ElseIf DOWN And (CURDIR1=3 Or CURDIR1=1) : NEWDIR1= 3
ElseIf LEFT And (CURDIR1=0 Or CURDIR1=2) : NEWDIR1= 4
EndIf
EndIf
If NUMP = 2 : UP= 0 : RIGHT= 0 : DOWN= 0 : LEFT= 0
If ((KeyboardPushed(#PB_Key_W) And P2ID=0) Or (KeyboardPushed(#PB_Key_Up) And P2ID=1) Or (JY0=1 And P2ID=2) Or (JY1=1 And P2ID=3)) : UP= 1 : EndIf
If ((KeyboardPushed(#PB_Key_D) And P2ID=0) Or (KeyboardPushed(#PB_Key_Right) And P2ID=1) Or (JX0=1 And P2ID=2) Or (JX1=1 And P2ID=3)) : RIGHT= 1 : EndIf
If ((KeyboardPushed(#PB_Key_S) And P2ID=0) Or (KeyboardPushed(#PB_Key_Down) And P2ID=1) Or (JY0=-1 And P2ID=2) Or (JY1=-1 And P2ID=3)) : DOWN= 1 : EndIf
If ((KeyboardPushed(#PB_Key_A) And P2ID=0) Or (KeyboardPushed(#PB_Key_Left) And P2ID=1) Or (JX0=-1 And P2ID=2) Or (JX1=-1 And P2ID=3)) : LEFT= 1 : EndIf
If DIAGONALS
If UP And RIGHT : If CURDIR2<>4 And CURDIR2<>6 : NEWDIR2= 5 : EndIf
ElseIf RIGHT And DOWN : If CURDIR2<>5 And CURDIR2<>7 : NEWDIR2= 6 : EndIf
ElseIf DOWN And LEFT : If CURDIR2<>4 And CURDIR2<>6 : NEWDIR2= 7 : EndIf
ElseIf LEFT And UP : If CURDIR2<>5 And CURDIR2<>7 : NEWDIR2= 8 : EndIf
ElseIf UP And CURDIR2<> 0 And CURDIR2 <> 2 : NEWDIR2= 1
ElseIf RIGHT And CURDIR2<> 1 And CURDIR2 <> 3 : NEWDIR2= 2
ElseIf DOWN And CURDIR2<> 0 And CURDIR2 <> 2 : NEWDIR2= 3
ElseIf LEFT And CURDIR2<> 1 And CURDIR2 <> 3 : NEWDIR2= 4
EndIf
Else
If UP And (CURDIR2=3 Or CURDIR2=1) : NEWDIR2= 1
ElseIf RIGHT And (CURDIR2=0 Or CURDIR2=2) : NEWDIR2= 2
ElseIf DOWN And (CURDIR2=3 Or CURDIR2=1) : NEWDIR2= 3
ElseIf LEFT And (CURDIR2=0 Or CURDIR2=2) : NEWDIR2= 4
EndIf
EndIf
Else
NEWDIR2= GETAIMOVE(CURDIR2,SNAKE1(),SNAKE2(),WRAP) ; HERE THE AI CREATES THE MOVEMENTS (NO DIAGONALS)
EndIf
If (NEWDIR1 Or NEWDIR2) And Not ALREADY And CLICK : PlaySound(1) : EndIf
Else ; NOT YET PLAYING, INSTEAD PROCESSING FRONTEND KEYBOARD INPUT
If PLAYING = 3
If KeyboardPushed(#PB_Key_Space) : If CLICK : PlaySound(1) : EndIf
Goto RESTART
ElseIf KeyboardPushed(#PB_Key_Return) : PLAYING= 0 : If CLICK : PlaySound(1) : EndIf
GETOFFKEY()
EndIf
Else
If ((KeyboardPushed(#PB_Key_RightAlt) Or KeyboardPushed(#PB_Key_LeftAlt)) And KeyboardPushed(#PB_Key_Return)) Or KeyboardPushed(#PB_Key_F) Or KeyboardPushed(#PB_Key_S) Or KeyboardPushed(#PB_Key_W)
If CLICK : PlaySound(1) : EndIf ; F, S, W, OR Alt+Enter TO TOGGLE FULLSCRFEEN
GETOFFKEY()
CompilerIf #PB_Compiler_OS=#PB_OS_Windows
CloseScreen()
If Not FULL : CloseWindow(0) : FULL= 1 : OpenScreen(800,600,32,"SURROUND")
Else : FULL= 0 : OpenWindow(0,0,0,800,600,WINTITLE$,#FLAGS)
OpenWindowedScreen(WindowID(0),0,0,800,600,#True,0,0) : CLEARQUESC()
EndIf
CREATESPRITES() ; MUST RECREATE SCORING DIGITS AND PLAYER'S HEAD AND TRAIL SQUARES
CompilerEndIf
ElseIf KeyboardPushed(#PB_Key_F1) : If CLICK : PlaySound(1) : EndIf
NUMP= 3- NUMP : GETOFFKEY() : If NUMP= 1 : DIAGONALS= 0 : EndIf
ElseIf KeyboardPushed(#PB_Key_F2) : If CLICK : PlaySound(1) : EndIf
SPEEDUP= 1- SPEEDUP : GETOFFKEY()
ElseIf KeyboardPushed(#PB_Key_F3) : If CLICK : PlaySound(1) : EndIf
ERASE= 1- ERASE : GETOFFKEY()
ElseIf KeyboardPushed(#PB_Key_F4) : If CLICK : PlaySound(1) : EndIf
WRAP= 1- WRAP : GETOFFKEY()
ElseIf KeyboardPushed(#PB_Key_F5) And NUMP = 2: If CLICK : PlaySound(1) : EndIf
DIAGONALS= 1- DIAGONALS : GETOFFKEY()
ElseIf KeyboardPushed(#PB_Key_F6) : If CLICK : PlaySound(1) : EndIf
P1ID= P1ID+ 1 : If (P1ID = 2 And NUMJOY = 0) Or (P1ID = 3 And NUMJOY < 2) Or P1ID= 4 : P1ID= 0 : EndIf
GETOFFKEY() : If P1ID = P2ID : GOOD= 0
Else : GOOD= 1 : EndIf
ElseIf KeyboardPushed(#PB_Key_F7) And NUMP = 2 : If CLICK : PlaySound(1) : EndIf
P2ID= P2ID+ 1 : If (P2ID = 2 And NUMJOY = 0) Or (P2ID = 3 And NUMJOY < 2) Or P2ID= 4 : P2ID= 0 : EndIf
GETOFFKEY() : If P1ID = P2ID : GOOD= 0
Else : GOOD= 1 : EndIf
ElseIf KeyboardPushed(#PB_Key_Space) And ((GOOD And NUMP=2) Or NUMP=1) : If CLICK : PlaySound(1) : EndIf
; CHECK FOR START OF GAME, PLAYING<>0 SIGNALS IN GAME
RESTART: : PLAYING= 1 : P1S= 0 : P2S= 0 : GETOFFKEY()
NEXTPOINT: : P1DEAD= 0 : P2DEAD= 0 : NEWDIR1= 0 : NEWDIR2= 0
SNAKE1(0)= 1 ; CREATE INITIAL SNAKES. SNAKE(0)= LENGTH OF SNAKE, SNAKE(>0) = X{0-39}*100+ Y{0-29}
CURDIR1= 1 : DX1= 1 : DY1= 0 : HP1X= 9 : HP1Y= 15 : SNAKE1(1)= HP1X*100+ HP1Y
SNAKE2(0)= 1 : CURDIR2= 3 : DX2= -1 : DY2= 0 : HP2X= 30 : HP2Y= 15 : SNAKE2(1)= HP2X*100+ HP2Y
; SET INITIAL MOVE SPEED. IF SPEEDUP IS SET, THEN USE VELCHGTIM AS TIMER FOR NEXT APPLICATION OF SPINC
SPEED= 330 : SPINC= -60 : VELCHGTIM= 20000
; ATLIMIT GETS SET TO 1 WHEN SPEED INCREASE IS AT MAXIMUM, SET NEXT SPEED CHANGE TIME, SET NEXT SNAKE MOVE TIME
ATLIMIT= 0 : NEXSPC= ElapsedMilliseconds()+ VELCHGTIM : NEXMOV= ElapsedMilliseconds()+ SPEED
EndIf
EndIf
EndIf
; FINISHED GATHERING AND PROCESSING USER INPUT, NEXT PROCESS THE CONTEXTUAL AND TIMED EVENTS
CLEARQUESC() ; EMPTY EVENT QUEUE ONE LAST TIME BEFORE GRAPHICS CONSTRUCTION
; CREATE SCREEN BASED ON PARAMETERIZED STATE VARIABLES
DRAWSCREEN(PLAYING,SNAKE1(),SNAKE2(),NUMP,SPEEDUP,ERASE,WRAP,DIAGONALS,GOOD,P1ID,P2ID,P1S,P2S)
If ElapsedMilliseconds() > NEXMOV And PLAYING = 1 And Not (P1DEAD Or P2DEAD) ; IS TIME TO MOVE SNAKES?
If NEWDIR1 : CURDIR1= NEWDIR1-1 : NEWDIR1= 0 : EndIf
Select CURDIR1 ; CALCULATE NEXT SNAKE 1 HEAD POSITION.
Case 0 : DX= 0 : DY= -1 ; GOING UP
Case 1 : DX= 1 : DY= 0 ; RIGHT
Case 2 : DX= 0 : DY= 1 ; DOWN
Case 3 : DX= -1 : DY= 0 ; LEFT
Case 4 : DX= 1 : DY= -1 ; NE
Case 5 : DX= 1 : DY= 1 ; SE
Case 6 : DX= -1 : DY= 1 ; SW
Case 7 : DX= -1 : DY= -1 ; NW
EndSelect
HP1X= HP1X+ DX : HP1Y= HP1Y+ DY
If WRAP
If HP1X<0 : HP1X= 39
ElseIf HP1X > 39 : HP1X= 0 : EndIf
If HP1Y<0 : HP1Y= 29
ElseIf HP1Y > 29 : HP1Y= 0 : EndIf
Else
If HP1X<0 Or HP1X=40 Or HP1Y<0 Or HP1Y=30 : P1DEAD= 1 : P2S+ 1 : EndIf
EndIf
If NEWDIR2 : CURDIR2= NEWDIR2-1 : NEWDIR2= 0 : EndIf
Select CURDIR2 ; CALCULATE NEXT SNAKE 2 HEAD POSITION
Case 0 : DX= 0 : DY= -1 ; GOING UP
Case 1 : DX= 1 : DY= 0 ; RIGHT
Case 2 : DX= 0 : DY= 1 ; DOWN
Case 3 : DX= -1 : DY= 0 ; LEFT
Case 4 : DX= 1 : DY= -1 ; NE
Case 5 : DX= 1 : DY= 1 ; SE
Case 6 : DX= -1 : DY= 1 ; SW
Case 7 : DX= -1 : DY= -1 ; NW
EndSelect
HP2X= HP2X+ DX : HP2Y= HP2Y+ DY
If WRAP
If HP2X<0 : HP2X= 39
ElseIf HP2X > 39 : HP2X= 0 : EndIf
If HP2Y<0 : HP2Y= 29
ElseIf HP2Y > 29 : HP2Y= 0 : EndIf
Else
If HP2X<0 Or HP2X=40 Or HP2Y<0 Or HP2Y=30 : P2DEAD= 1 : P1S+ 1 : EndIf
EndIf
For IND= 1 To SNAKE1(0) ; CHECK FOR ANY SNAKE HITTING ITSELF OR ANOTHER
If SNAKE1(IND)/100=HP1X And SNAKE1(IND)%100=HP1Y : P1DEAD= 1 : P2S+ 1 : EndIf
If SNAKE1(IND)/100=HP2X And SNAKE1(IND)%100=HP2Y : P2DEAD= 1 : P1S+ 1 : EndIf
Next IND
For IND= 1 To SNAKE2(0)
If SNAKE2(IND)/100=HP1X And SNAKE2(IND)%100=HP1Y : P1DEAD= 1 : P2S+ 1 : EndIf
If SNAKE2(IND)/100=HP2X And SNAKE2(IND)%100=HP2Y : P2DEAD= 1 : P1S+ 1 : EndIf
Next IND
BLANK= 0
If ERASE And ((P1ID=0 And KeyboardPushed(#PB_Key_LeftControl)) Or (P1ID=1 And KeyboardPushed(#PB_Key_RightControl)) Or (P1ID=2 And JB0) Or(P1ID=3 And JB1))
BLANK= 1
EndIf
UPDATESNAKE(SNAKE1(),HP1X,HP1Y,BLANK) : BLANK= 0
If ERASE And ((P2ID=0 And KeyboardPushed(#PB_Key_LeftControl)) Or (P2ID=1 And KeyboardPushed(#PB_Key_RightControl)) Or (P2ID=2 And JB0) Or(P2ID=3 And JB1))
BLANK= 1
EndIf
UPDATESNAKE(SNAKE2(),HP2X,HP2Y,BLANK)
If P1DEAD Or P2DEAD
CLEARQUESC()
DRAWSCREEN(PLAYING,SNAKE1(),SNAKE2(),NUMP,SPEEDUP,ERASE,WRAP,DIAGONALS,GOOD,P1ID,P2ID,P1S,P2S)
If DIESO : PlaySound(3) : EndIf
Delay(1000) : CLEARQUESC()
If ((P1S > 9) Or (P2S > 9)) And (P1S <> P2S)
PLAYING= 3 : GETOFFKEY()
Else
Goto NEXTPOINT
EndIf
EndIf
If BOOP : PlaySound(2) : EndIf
NEXMOV= ElapsedMilliseconds()+ SPEED
EndIf
If SPEEDUP And PLAYING = 1 And Not (P1DEAD Or P2DEAD) And Not ATLIMIT
If ElapsedMilliseconds() > NEXSPC ; CHECK IF TIME TO UPDATE SNAKE SPEED
NEXSPC= ElapsedMilliseconds()+ VELCHGTIM
SPEED= SPEED+ SPINC
If SPEED = 30 : ATLIMIT= 1 : EndIf
EndIf
EndIf
If ElapsedMilliseconds() < RELEASE ; LIMITS FPS TO 50 MAX
Repeat : Delay(2) : Until ElapsedMilliseconds() > RELEASE
EndIf
Until BYEBYE
EndProcedure ;(MAIN)
; ***********************************
; **** END OF PROCEDURES SECTION ****
; ***********************************
; PROGRAM'S GLOBAL ENVIRONMENT RESOURCE SETUP IS FINISHED, CONTINUE EXECUTION.
MAIN()
End
Robust error checking was the first thing to go, but with a program this simple and closed, it causes no major problems.
There are no Macs in my world, so if you have one this source will undoubtedly need changing.