-- *********************************************************************** -- * * -- * ARENA.EX The Happy Hippy (C) 1998 * -- * * -- *********************************************************************** constant VERSION = 1.11 -- DO NOT CHANGE THIS VALUE OR CODE WON'T RUN ! -- *********************************************************************** -- * * -- * Description : A Demonstration Euphoria Pixel Bot Arena Program * -- * Version : 1.11 - 30th November 1998 * -- * * -- * Filename : arena.ex * -- * Source Code : Euphoria 2.00 * -- * * -- * Author : The Happy Hippy * -- * Copyright : The Happy Hippy (C) 1998 * -- * Contact : hippy@psynet.net * -- * * -- * Licence : This program is made available to the public as * -- * FreeWare and does not require a licence or the * -- * payment of royalties for its use. The program may * -- * be installed and used on any number of computers. * -- * * -- * You may modify this code and comments as you wish * -- * and may use it as the basis of any alternative * -- * Arena program you develop free of charge and * -- * royalties, however ... * -- * * -- * Warning : You may not under any circumstances distribute or * -- * publish anything derived from this work using the * -- * title 'The Pixel Bot Project Arena Program' or * -- * similar and you must not distribute or publish * -- * anything derived from this work that is identified * -- * using the filename of "arena.ex" without written * -- * permission from the original author. * -- * * -- * If you have a suggestion to make regarding the * -- * operation or functionality of this program then * -- * please contact the author ( hippy@psynet.net ) * -- * and your suggestion will be considered. * -- * * -- * Trademarks : See http://www.psynet.net/hippy/pixelbot.htm for * -- * details of Copyright and Trademark assignments. * -- * * -- * Y2K Issues : This code does not use dates for any purpose and * -- * is therefore, by default, millennium compliant. * -- * * -- * For further information about the Euphoria Pixel Bot project * -- * please look at http://www.psynet.net/hippy/pixelbot.htm * -- * * -- * For further information about the Euphoria language please look * -- * at http://members.aol.com/FilesEu * -- * * -- *********************************************************************** -- *********************************************************************** -- * * -- * KNOWN BUGS * -- * * -- * comments There are still areas that require more * -- * comments than are present. * -- * * -- * Fire_Laser() Doesn't cause damage in a rational way. * -- * * -- * INCREASE_LASER_PER_TICK These constant values need to * -- * INCREASE_HEALTH_PER_TICK better defined. * -- * * -- *********************************************************************** -- *********************************************************************** -- * * -- * RELEASE HISTORY * -- * * -- * 1.11 30 Nov 1998 Minor changes to This_Pixel_Bot_Wins() * -- * for better generic handling. * -- * * -- * Included winner detection when all other * -- * Pixel Bot's destroyed. * -- * * -- * Winning Pixel Bot's name displayed. * -- * * -- * 1.10 26 Nov 1998 Maze exit points now generated and the * -- * positioning of Pixel Bots at start set * -- * to reflect maze exit location. * -- * * -- * Winner detected when parent or non-clone * -- * Pixel Bot moves onto maze exit. * -- * * -- * Variable run speed depending on arena * -- * scenario. * -- * * -- * 1.09 21 Nov 1998 A Maze Generator program has been added * -- * to provide mazes to the arena program - * -- * requires maze.e to run. * -- * * -- * Disables Ctrl-Break key. * -- * * -- * 1.08 16 Nov 1998 The Scan_For() function has now been * -- * completed. * -- * * -- * Added code to Listen_To() to stop a * -- * Pixel Bot listening to itself. * -- * * -- * Added code to Ignore_From() to flush * -- * the message queue and clear the whole * -- * Listen To list. * -- * * -- * Changed Kill_Clone() so any Pixel Bot * -- * can kill itself. * -- * * -- * Added Graphical Interface API routines * -- * to show winning and losing Pixel Bots. * -- * * -- * 1.07 15 Nov 1998 The Scan() function has been removed; * -- * this should not cause problems for any * -- * early Pixel Bot designs as Scan() was * -- * working the same as Look() and should * -- * not ( generally ) have been used. If a * -- * compilation error occurs; change Scan() * -- * to Look(). * -- * * -- * The Health() function has been added. * -- * * -- * 1.06 14 Nov 1998 Internal handling of Clone numbering * -- * has been rationalised. * -- * * -- * Included Welcome Screen and Arena rule * -- * settings - requires welcome.e to run. * -- * * -- * Option settings are saved between runs * -- * in the file ARENA.CFG; do not edit this * -- * file by hand. * -- * * -- * 1.05 13 Nov 1998 Added identifier for particular Pixel * -- * Bot related actions in calls to the * -- * Graphical Interface. * -- * * -- * An anomaly in Clone_Number() has been * -- * corrected; see the comments before the * -- * Clone_Number() function. * -- * * -- * 1.04 12 Nov 1998 Reduced public Graphical Interface API * -- * connection in arena.e so that there * -- * is less chance of a user hacking the * -- * source code of arena for information * -- * to accidentally use something in the * -- * Pixel Bot program that they shouldn't. * -- * * -- * Note : The intention is not to stop * -- * hacking, which is still encouraged, * -- * but to prevent misinformation being * -- * spread about what may or may not be * -- * of possible use to a Pixel Bot program. * -- * * -- * A lot more comments explaining the inner * -- * workings of the arena program have been * -- * added; there are still sections that do * -- * need additional comments. * -- * * -- * The code format has been changed so it * -- * is easier to find the code amongst the * -- * comments. * -- * * -- * Version control added. * -- * * -- * 1.03 12 Nov 1998 Removed redundant 'global' constants. * -- * * -- * Improved Kill() to recover used memory * -- * and prevent dead Pixel Bots from getting * -- * messages. * -- * * -- * 1.02 11 Nov 1998 Improved execution scheduling for dead * -- * clones; removes bugs where not all are * -- * always killed. * -- * * -- * Internal change so health rather than * -- * damage is maintained per Pixel Bot. * -- * * -- * Improved Graphical Interface API Spec. * -- * * -- * Added collision detection; no effect at * -- * present. * -- * * -- * Communications API implemented but not * -- * fully tested. * -- * * -- * 1.01 10 Nov 1998 Clone_This_Pixel_Bot() is renamed to * -- * Create_Clones() and slight code change * -- * made. Clone() renamed Clone_Number(). * -- * * -- * Communication API's created as skeleton * -- * code - these do nothing at present. * -- * * -- * Look_For() and Scan_For() created as * -- * skeleton code - these do nothing at * -- * present. * -- * * -- * 1.00 09 Nov 1998 First public release * -- * * -- * The basic prototype is complete with * -- * all the API supported if not implemented * -- * properly. Scan() is noticably incomplete * -- * and Fire_Laser() doesn't cause damage in * -- * a rational way. There is a distinct lack * -- * of explantory comments in the code. * -- * * -- *********************************************************************** -- *********************************************************************** -- * * -- * NOTES * -- * * -- * The arena program uses a lot of references to Rows and Columns; * -- * these indicate where things are in the Arena and Row 1, Column * -- * 1 represents the top-left corner of the arena. * -- * * -- * Rows increase down the arena and Columns increase across the * -- * arena to the right. * -- * * -- * The reason that Rows and Columns were chosen instead of the * -- * more traditional X and Y ( or is it Y and X ? ) is because I * -- * can never actually remember which runs vertically and which * -- * runs horizontally - using Rows and Columns makes it obvious * -- * which is which. * -- * * -- * The actual graphics screen uses a slightly different system of * -- * co-ordinates with the top-left of the screen being (0,0); the * -- * translaton of Rows and Columns into cartesian co-ordinates is * -- * the responsibility of the Graphical Interface - Everyone else * -- * should just use Rows and Columns as described above. * -- * * -- *********************************************************************** -- *********************************************************************** -- * * -- * DEFINE GLOBAL CONSTANTS * -- * * -- *********************************************************************** -- Boolean functions will always return either TRUE or FALSE and boolean -- variables may only be set to these values. global constant TRUE = 1 global constant FALSE = 0 -- These are the absolute directions that may be used by a Pixel Bot -- in calls to various routines and are the values returned when the -- Facing() function is called. They are defined global so they do not -- have to be repeated in the Pixel Bot program code. global constant NORTH = 0 global constant EAST = 1 global constant SOUTH = 2 global constant WEST = 3 -- These are the relative directions that may be used by a Pixel Bot -- in calls to various routines. They are defined global so they do -- not have to be repeated in the Pixel Bot program code. global constant AHEAD = 4 global constant RIGHT = 5 global constant BEHIND = 6 global constant LEFT = 7 -- These are objects other than Pixel Bots that may be found in the area -- Define objects that may be found in the arena. They are defined global -- so they do not need to be repeated in the Pixel Bot program code. -- Routines such as Look() return a number that represents a Pixel Bot or -- an object; if the number is positive it represents the identifying -- number of a particular Pixel Bot so al other objects have to be -- defined as less than or equal to zero. -- Zero is an excellent choice to mean there is no object. -- The other objects that could exist can be given any number providing -- that it is negative. Additional objects may easily be added. global constant NOTHING = 0 -- Nothing global constant WALL = -1 -- A wall global constant EXIT_POINT = -2 -- The maze exit global constant TELEPORT = -3 -- A Teleporter -- These are just defined as examples of objects that may appear in -- the future; they are not presently supported. -- global constant LANDMINE = -4 -- A land mine -- global constant MEDI_PACK = -5 -- A medical pack -- global constant LASER_CHARGE = -6 -- Extra laser charge -- global constant ARMOUR = -7 -- Armour -- *********************************************************************** -- * * -- * DEFINE TYPE DEFINITIONS * -- * * -- *********************************************************************** -- Define the traditional boolean type that can have either a TRUE or a -- FALSE value. global type boolean(integer x) return x = TRUE or x = FALSE end type -- *********************************************************************** -- * * -- * ARENA PROGRAM DATABASE * -- * * -- *********************************************************************** sequence maze sequence The_World integer Pixel_Bot_Count sequence Pixel_Bot_Init_Call_Id_List sequence Pixel_Bot_Exec_Call_Id_List sequence Pixel_Bot_Name_List sequence Pixel_Bot_Facing_List sequence Pixel_Bot_Health_List sequence Pixel_Bot_Attacked_By_List sequence Pixel_Bot_Laser_Power_List sequence Pixel_Bot_Row_List sequence Pixel_Bot_Col_List sequence Pixel_Bot_Clone_List sequence Pixel_Bot_Message_List sequence Pixel_Bot_Listen_List sequence Run_Order_List integer Parents_Alive_Count integer Pixel_Bots_Alive_Count boolean Game_Create_A_Maze boolean Game_Create_A_Maze_Exit integer Game_Maximum_Number_Of_Clones_Allowed sequence Pixel_Bot_Places integer This_Pixel_Bot boolean Made_A_Move boolean Made_A_Turn boolean Sent_A_Message atom Tick_Time boolean Finished boolean Too_Slow integer Limbo integer Group -- Group number for Bots Group = -1 -- First group is 0 constant NORMAL_TICK = 0.05 -- 20 ticks per second constant FAST_TICK = 0.00 -- Run as fast as possible constant INCREASE_LASER_PER_TICK = 0.5 -- 40 seconds to 100% constant INCREASE_HEALTH_PER_TICK = 1 -- 20 seconds to 100% constant LOOK_RANGE = 64 -- constant LASER_RANGE = 64 -- *********************************************************************** -- * * -- * INITIALIZE THE DATABASE * -- * * -- *********************************************************************** The_World = repeat( repeat(0,640) , 480) Pixel_Bot_Count = 0 Pixel_Bot_Init_Call_Id_List = {} Pixel_Bot_Exec_Call_Id_List = {} Pixel_Bot_Name_List = {} Pixel_Bot_Facing_List = {} Pixel_Bot_Health_List = {} Pixel_Bot_Attacked_By_List = {} Pixel_Bot_Laser_Power_List = {} Pixel_Bot_Row_List = {} Pixel_Bot_Col_List = {} Pixel_Bot_Clone_List = {} Pixel_Bot_Message_List = {} Pixel_Bot_Listen_List = {} Run_Order_List = {} Parents_Alive_Count = 0 Pixel_Bots_Alive_Count = 0 -- *********************************************************************** -- * * -- * VERSION CONTROL * -- * * -- *********************************************************************** -- Version control has been added so that the user can be told if he is -- running his Pixel Bots with an Arena program that is out of date or -- or isn't the latest version. If the versions are too incompatible it -- is likely that the Pixel Bot program won't run at all without giving -- a compilation error, however, this is the best way to try and keep -- everyone involved in the project up to date. -- Once we escape the 1.XX versions, and begin 2.XX versions, there will -- be a lot less changes occuring on a day-to-day basis and there should -- be less conflicts with users trying to run out-of-date code. -- Define the version numbers of the welcome.e program that we will -- support in this version constant WELCOME_VERSIONS = { 1.00 } -- Define the version numbers of the frontend.e program that we will -- support in this version constant FRONT_END_VERSIONS = { 1.06 } -- Define the version numbers of the Pixel Bot programs that we will -- support in this arena program constant PIXEL_BOT_VERSIONS = { 1.02 , 1.01 } -- If we get an error; report it and abort the arena program procedure Fatal(sequence s) puts(1,"\n") puts(1,"Arena Program ( arena.ex ) version " & sprintf("%f",VERSION) & "\n") puts(1,"\n" & s & "\n\n") abort(1) end procedure procedure Version_Control(sequence List, atom Version, sequence What) atom Latest atom Earliest Latest = List[1] Earliest = List[length(List)] if compare(Version,Latest) > 0 then Fatal("The version of the arena.ex program is out of date and " & "cannot be used with your " & What ) end if if compare(Version,Earliest) < 0 then Fatal("Your " & What & " is out of date and cannot be used with " & "this version of the arena.ex program") end if end procedure -- *********************************************************************** -- * * -- * SCREEN HANDLING FRONT END * -- * * -- *********************************************************************** -- We must include the actual Graphical Interface code before we try and -- use it. include frontend.e -- These are the names of all the routines that are in the Graphical -- Interface that we will be using from within the Arena Program. They -- are described as integer variables because they will contain a number -- that is obtained from the Graphical Interface itself. integer Enter_Graphics_Mode integer Leave_Graphics_Mode integer Create_Wall integer Create_Exit_Point integer Create_Teleport integer Create_Pixel_Bot integer Make_Pixel_Bot_Face integer Move_Pixel_Bot_From integer Move_Pixel_Bot_To integer Damage_Pixel_Bot_At integer Destroy_Pixel_Bot_At integer Synchronise_Front_End integer Kick_Front_End integer Fired_Laser integer Winning_Pixel_Bot_At integer Losing_Pixel_Bot_At -- We must link into the Graphical Interface to determine the -- identifying numbers for all the routines that we are going to use -- within the Arena Program. We call the GUI_Enquiry() function with -- the name of the routine whose identifying number we wish to obtain. procedure Link_To_The_Front_End() -- Check we have compatible versions of the Arena program and the -- Graphical Interface Version_Control( FRONT_END_VERSIONS, GUI_Enquiry("Version"), "Graphical Interface program ( frontend.e )" ) Enter_Graphics_Mode = GUI_Enquiry("Enter_Graphics_Mode") Leave_Graphics_Mode = GUI_Enquiry("Leave_Graphics_Mode") Create_Wall = GUI_Enquiry("Create_Wall") Create_Exit_Point = GUI_Enquiry("Create_Exit_Point") Create_Teleport = GUI_Enquiry("Create_Teleport") Create_Pixel_Bot = GUI_Enquiry("Create_Pixel_Bot") Make_Pixel_Bot_Face = GUI_Enquiry("Make_Pixel_Bot_Face") Move_Pixel_Bot_From = GUI_Enquiry("Move_Pixel_Bot_From") Move_Pixel_Bot_To = GUI_Enquiry("Move_Pixel_Bot_To") Damage_Pixel_Bot_At = GUI_Enquiry("Damage_Pixel_Bot_At") Destroy_Pixel_Bot_At = GUI_Enquiry("Destroy_Pixel_Bot_At") Synchronise_Front_End = GUI_Enquiry("Synchronise_Front_End") Kick_Front_End = GUI_Enquiry("Kick_Front_End") Fired_Laser = GUI_Enquiry("Fired_Laser") Winning_Pixel_Bot_At = GUI_Enquiry("Winning_Pixel_Bot_At") Losing_Pixel_Bot_At = GUI_Enquiry("Losing_Pixel_Bot_At") end procedure -- When we wish to call a routine in the Graphical Interface we will -- use a call to GUI() using the variable that contains the identifying -- number of the routine we want to use. For example ... -- If there is a routine in the Graphical Interface called Splat() which -- draws an icon at a particular row and column on the screen then it -- may be defined in frontend.e as ... -- function Splat(integer Row, integer Column) -- -- Code here to draw the icon at the right place -- end function -- We would create a reference to it in arena.ex using ... -- integer Splat -- Splat = GUI_Enquiry("Splat") -- And we would call Splat() to draw an Icon at Row 7, Column 20 by using -- a call to GUI() such as ... -- GUI( Splat , { 7 , 20 } ) -- Note that the two arguments that are expected by the Splat() routine -- in frontend.e have to be enclosed in braces in the GUI() call in the -- arena.e program. -- *********************************************************************** -- * * -- * WELCOME SCREEN HANDLING FRONT END * -- * * -- *********************************************************************** -- We must include the actual Welcome Interface code before we try and -- use it. include welcome.e -- These are the names of all the routines that are in the Welcome -- Interface that we will be using from within the Arena Program. They -- are described as integer variables because they will contain a number -- that is obtained from the Welcome Interface itself. integer Show_Welcome_Screen integer Change_Options integer Option_Setting integer Read_Options_From_Disk integer Write_Options_To_Disk -- We must link into the Welcome Interface to determine the -- identifying numbers for all the routines that we are going to use -- within the Arena Program. We call the Welcome_Enquiry() function with -- the name of the routine whose identifying number we wish to obtain. procedure Link_To_Welcome() -- Check we have compatible versions of the Arena program and the -- Welcome Interface Version_Control( WELCOME_VERSIONS, Welcome_Enquiry("Version"), "Welcome Interface program ( welcome.e )" ) Show_Welcome_Screen = Welcome_Enquiry("Show_Welcome_Screen") Change_Options = Welcome_Enquiry("Change_Options") Option_Setting = Welcome_Enquiry("Option_Setting") Read_Options_From_Disk = Welcome_Enquiry("Read_Options_From_Disk") Write_Options_To_Disk = Welcome_Enquiry("Write_Options_To_Disk") end procedure -- The Welcome Interface should be used just as the Graphical Interface -- is; see the description of the Graphical Interface above. -- *********************************************************************** -- * * -- * UTILITY API FUNCTIONS * -- * * -- *********************************************************************** -- These utility routines are placed at the top of he file because thay -- are also used by a number of other routines used internally by the -- arena program itself. -- Returns an integer random number that will always have a value that is -- between the minimum and maximum value inclusive. global function Random(integer minimum,integer maximum) return ( rand( (maximum-minimum)+1 ) + minimum - 1 ) end function -- Return TRUE for n% of the calls made to this function otherwise return -- FALSE. global function Percent(integer n) return ( Random(1,100) <= n ) end function -- *********************************************************************** -- * * -- * PIXEL BOT WORLD CREATION UTILITY ROUTINES * -- * * -- *********************************************************************** -- Draw walls all the way around the arena. procedure Create_The_Edges_Of_The_World() The_World[1] = repeat( WALL, 640 ) The_World[480] = repeat( WALL, 640 ) for Col = 1 to 640 do GUI(Create_Wall,{1,Col}) GUI(Create_Wall,{480,Col}) end for for Row = 1 to 480 do The_World[Row][1] = WALL The_World[Row][640] = WALL GUI(Create_Wall,{Row,1}) GUI(Create_Wall,{Row,640}) end for end procedure -- Draw a wall down the arena, from the north to the south. procedure Build_Wall_Down(integer Row_From, integer Row_To, integer Col) for Row = Row_From to Row_To do The_World[Row][Col] = WALL GUI(Create_Wall,{Row,Col}) end for end procedure -- Draw a wall across the arena, from west to east. procedure Build_Wall_Across(integer Col_From, integer Col_To, integer Row) for Col = Col_From to Col_To do The_World[Row][Col] = WALL GUI(Create_Wall,{Row,Col}) end for end procedure -- *********************************************************************** -- * * -- * MAZE CREATION ROUTINES * -- * * -- *********************************************************************** include maze.e procedure Create_And_Draw_A_Maze() integer walls maze = Create_A_Maze(80) for Row=1 to 12 do for Col=1 to 16 do walls = maze[Row][Col] if and_bits(walls,1) then Build_Wall_Across((Col-1)*40+1,Col*40,(Row-1)*40+1) end if if and_bits(walls,8) then Build_Wall_Down((Row-1)*40+1,Row*40,(Col-1)*40+1) end if end for end for end procedure -- *********************************************************************** -- * * -- * PIXEL BOT WORLD CREATION ROUTINES * -- * * -- *********************************************************************** -- The arena is divided up into nine squares; anyone of these squares is -- where a maze exit could be placed. Sqaure 9 is in the centre as it is -- the least likely place to ever place an exit; this means that the exit -- can be placed in either squares 1 to 8 or 1 to 9 if we do allow the -- centre square to be used - this simplifies the creation of a random -- square to choose; set the top limit to 8 or 9 respectively. -- When a maze exit is placed in a particular square; Pixel Bots will -- only be placed in certain other sqaures; this is so that the chance -- of any particular Pixel Bot getting a straight run at the exit or -- any other advantage is reduced. -- 1 213 214 415 426 640 Exit Pixel Bots -- ________ _________ ________ -- 1 | | | | 1 5 7 -- | 1 | 2 | 3 | 2 6 8 -- 213 |________|_________|________| 3 4 7 -- 214 | | | | 4 3 8 -- | 4 | 9 | 5 | 5 1 6 -- 427 |________|_________|________| 6 2 5 -- 428 | | | | 7 1 3 -- | 6 | 7 | 8 | 8 2 4 -- 480 |________|_________|________| None Any -- The Identify_Places() function returns a set of co-ordinate type -- points that bound the chosen squares; each set of co-ordinates are -- added to a sequence that is returned. Each co-ordinate entry has -- the following format ... -- { min row , max row , min col , max col } -- The row and col values are fudged to ensure that we do not include -- the edges of the arena which makes the use of these coordinates -- when we come to use them to place things in the arena. function Identify_Places(sequence squares) sequence List_Of_Places sequence add integer n List_Of_Places = {} for square = 1 to length(squares) do n = squares[square] if n = 1 then add = { 1,159 , 1,213 } elsif n = 2 then add = { 1,159 , 214,427 } elsif n = 3 then add = { 1,159 , 428,640 } elsif n = 4 then add = { 160,319 , 1,213 } elsif n = 9 then add = { 160,319 , 214,427 } elsif n = 5 then add = { 160,319 , 428,640 } elsif n = 6 then add = { 320,480 , 1,213 } elsif n = 7 then add = { 320,480 , 214,427 } elsif n = 8 then add = { 320,480 , 428,640 } else add = { 1,480 , 1,640 } end if if add[1] <= 1 then add[1] = 2 end if if add[2] >= 480 then add[2] = 479 end if if add[3] <= 1 then add[3] = 2 end if if add[4] >= 640 then add[4] = 639 end if List_Of_Places = append(List_Of_Places,add) end for return List_Of_Places end function -- Given a list of possible co-ordinates we find a point on the arena -- that is not occupied. -- Firstly we choose one of a list of random co-ordinates and then we -- choose a random location within that sqaure; we keep repeating this -- process until we find a ocation that is not already occupied. function Find_Place(sequence Places ) integer Done integer Row integer Col integer r Done = FALSE while not Done do r = Random(1,length(Places)) Row = Random(Places[r][1],Places[r][2]) Col = Random(Places[r][3],Places[r][4]) if The_World[Row][Col] = 0 then -- If empty then Done = TRUE -- Finished end if end while return { Row,Col } end function -- We determine a sqaure in the arena in which to place our maze exit -- and determine which squares our Pixel Bots may subsequently be -- placed in. -- We then find a random locaton with the chosen square for the exit -- and create it. procedure Create_A_Maze_Exit() integer r sequence Place integer Row integer Col r = Random(1,8) if r = 1 then Pixel_Bot_Places = Identify_Places( { 5,7 } ) elsif r = 2 then Pixel_Bot_Places = Identify_Places( { 6,8 } ) elsif r = 3 then Pixel_Bot_Places = Identify_Places( { 4,7 } ) elsif r = 4 then Pixel_Bot_Places = Identify_Places( { 3,8 } ) elsif r = 5 then Pixel_Bot_Places = Identify_Places( { 1,6 } ) elsif r = 6 then Pixel_Bot_Places = Identify_Places( { 2,5 } ) elsif r = 7 then Pixel_Bot_Places = Identify_Places( { 1,3 } ) elsif r = 8 then Pixel_Bot_Places = Identify_Places( { 2,4 } ) end if Place = Find_Place( Identify_Places( { r } ) ) Row = Place[1] Col = Place[2] The_World[Row][Col] = EXIT_POINT GUI(Create_Exit_Point,{Row,Col}) end procedure -- We need to create the Pixel Bot world. -- Firstly we must link in to the Graphical Interface routines them- -- selves and then we should enter the appropriate graphics screen -- mode ( 640 x 480, 16 colour mode at present ). -- If we need a maze we will create one and draw the walls of the -- maze. -- We must then create the edges of the arena. This is extremely -- important because the Pixel Bots will keep moving if they are -- not blocked. Whilst watching them fall off the edge of the -- world would be amusing it would also increase the complexity -- of the code because we would have to specifically handle the -- case when they were actually at the extremes of the world. By -- ensuring they never reach these extremities ( because there's -- a wall there ) the code never needs to check for this case -- as it can't occur. procedure Create_The_World() GUI(Enter_Graphics_Mode,{}) if Game_Create_A_Maze then Create_And_Draw_A_Maze() end if if Game_Create_A_Maze_Exit then Create_A_Maze_Exit() Tick_Time = FAST_TICK else Pixel_Bot_Places = Identify_Places( { 0 } ) -- Anywhere Tick_Time = NORMAL_TICK end if Create_The_Edges_Of_The_World() end procedure -- *********************************************************************** -- * * -- * OBSERVATION API ROUTINES * -- * * -- *********************************************************************** -- Returns TRUE if the Pixel Bot is damaged ( ie health is below 25% ) global function Damaged() return ( Pixel_Bot_Health_List[This_Pixel_Bot] <= 25.0 ) end function -- Returns a value indicating whichever way the Pixel Bot is facing; -- 0=North, 1=East, 2=South and 3 = West. global function Facing() return Pixel_Bot_Facing_List[This_Pixel_Bot] end function function Blocked_By_Relative(integer Inc_Row,integer Inc_Col) integer Row integer Col Row = Pixel_Bot_Row_List[This_Pixel_Bot] + Inc_Row Col = Pixel_Bot_Col_List[This_Pixel_Bot] + Inc_Col return The_World[Row][Col] end function -- Returns what ever is immediately ahead of the Pixel Bot; ie -- whatever it would 'step on' if it were to attempt to do a -- move forward. function Blocked_By() integer Direction Direction = Facing() if Direction = NORTH then return Blocked_By_Relative(-1,0) elsif Direction = SOUTH then return Blocked_By_Relative(+1,0) elsif Direction = EAST then return Blocked_By_Relative(0,+1) elsif Direction = WEST then return Blocked_By_Relative(0,-1) else return NOTHING -- Crashed ! end if end function -- Returns true if the Pixel Bot is unable to move forward because -- something is in its way. global function Blocked() return ( Blocked_By() != NOTHING ) end function -- Determine what the first thing is that can be found in range -- if we look north from the Pixel Bot's current position. function Look_North() integer Row integer Col integer Range Row = Pixel_Bot_Row_List[This_Pixel_Bot] Col = Pixel_Bot_Col_List[This_Pixel_Bot] Range = 1 while Range <= LOOK_RANGE do if The_World[Row-Range][Col] != 0 then return The_World[Row-Range][Col] else Range = Range + 1 end if end while return NOTHING end function -- Determine what the first thing is that can be found in range -- if we look south from the Pixel Bot's current position. function Look_South() integer Row integer Col integer Range Row = Pixel_Bot_Row_List[This_Pixel_Bot] Col = Pixel_Bot_Col_List[This_Pixel_Bot] Range = 1 while Range <= LOOK_RANGE do if The_World[Row+Range][Col] != 0 then return The_World[Row+Range][Col] else Range = Range + 1 end if end while return NOTHING end function -- Determine what the first thing is that can be found in range -- if we look east from the Pixel Bot's current position. function Look_East() integer Row integer Col integer Range Row = Pixel_Bot_Row_List[This_Pixel_Bot] Col = Pixel_Bot_Col_List[This_Pixel_Bot] Range = 1 while Range <= LOOK_RANGE do if The_World[Row][Col+Range] != 0 then return The_World[Row][Col+Range] else Range = Range + 1 end if end while return NOTHING end function -- Determine what the first thing is that can be found in range -- if we look west from the Pixel Bot's current position. function Look_West() integer Row integer Col integer Range Row = Pixel_Bot_Row_List[This_Pixel_Bot] Col = Pixel_Bot_Col_List[This_Pixel_Bot] Range = 1 while Range <= LOOK_RANGE do if The_World[Row][Col-Range] != 0 then return The_World[Row][Col-Range] else Range = Range + 1 end if end while return NOTHING end function -- Determine what the first thing is that can be found in range -- if we look in the specified direction from the Pixel Bot's -- current position. -- Nominally we can only look in four directions, 0=NORTH, 1=EAST, -- 2=SOUTH and 3=WEST, however we can also look in a direction that -- is relative to where the Pxel Bot is facing, 4 = AHEAD, 5=RIGHT, -- 6=BEHIND, 7=LEFT - This is acieved by using the recursive call ... -- return Look( remainder(Facing()+direction,4) ) -- The remainder(a,b) function returns the remainder after 'a' has -- been divided by 'b'. -- For example; assume the Pixel Bot is facing east and we want to -- look to the Pixel Bot's right ... -- return Look( remainder(Facing()+direction,4) ) -- return Look( remainder(EAST+RIGHT,4) ) -- return Look( remainder(1+5,4) ) -- return Look( remainder(6,4) ) -- return Look( 2 ) -- return Look( SOUTH ) global function Look(integer direction) if ( direction<0 ) or ( direction>7 ) then-- If invalid direction return NOTHING -- Say nothing is there else -- Otherwise if direction <= 3 then -- Specified N, E, S or W if direction = NORTH then return Look_North() elsif direction = EAST then return Look_East() elsif direction = SOUTH then return Look_South() elsif direction = WEST then return Look_West() else return NOTHING -- Crashed ! end if else -- Look relative to facing return Look( remainder(Facing()+direction,4) ) end if end if end function -- Looks in the direction specified to find the first object that is in -- range then, if that object is waht we expected it to be, returns -- TRUE otherwise returns FALSE. global function Look_For(integer direction,integer object) return ( Look(direction) = object ) end function -- We are going to scan in a particular direction to determine how far -- away ( in that direction ) a specified Pixel Bot is from this one. -- We firstly check that the target Pixel Bot exists and has not been -- killed. -- We secondly ensure that the direction we are going to scan in is a -- valid direction and, if it is a relative direction, we turn it into -- an absolute direction. -- We then calculate how much further to the north and east the target -- Pixel Bot is from ourselves ( a negative value means it is to the -- south or west respectively ). -- We then determine which distance needs to be returned to the caller; -- if we are looking north we use the north distance, to the south we -- use the north distance negated. We do the same with the east distance -- for east and wes ( negated ). global function Scan_For(integer direction,integer Target_Pixel_Bot) integer North_Gap integer East_Gap if ( Target_Pixel_Bot <= 0 ) or ( Target_Pixel_Bot > Pixel_Bot_Count ) then return 0 else if Pixel_Bot_Exec_Call_Id_List[Target_Pixel_Bot] = Limbo then return 0 else if ( direction<0 ) or ( direction>7 ) then-- If invalid direction return 0 -- Say nothing is there else -- Otherwise if direction <= 3 then -- Specified N, E, S or W North_Gap=Pixel_Bot_Row_List[Target_Pixel_Bot] - Pixel_Bot_Row_List[This_Pixel_Bot] East_Gap=Pixel_Bot_Col_List[Target_Pixel_Bot] - Pixel_Bot_Col_List[This_Pixel_Bot] if direction = NORTH then return North_Gap elsif direction = SOUTH then return -North_Gap elsif direction = EAST then return East_Gap elsif direction = WEST then return -East_Gap else return 0 -- Crashed ! end if else -- Look relative to facing return Look( remainder(Facing()+direction,4) ) end if end if end if end if end function -- *********************************************************************** -- * * -- * MOVEMENT API ROUTINES * -- * * -- *********************************************************************** procedure This_Pixel_Bot_Wins(integer Winning_Pixel_Bot) integer Row integer Col for Target_Pixel_Bot = 1 to Pixel_Bot_Count do Row = Pixel_Bot_Row_List[Target_Pixel_Bot] Col = Pixel_Bot_Col_List[Target_Pixel_Bot] if Target_Pixel_Bot = Winning_Pixel_Bot then GUI(Winning_Pixel_Bot_At,{Row,Col,Target_Pixel_Bot}) else if Pixel_Bot_Exec_Call_Id_List[Target_Pixel_Bot] != Limbo then if Pixel_Bot_Clone_List[Target_Pixel_Bot] <= 0 then GUI(Losing_Pixel_Bot_At,{Row,Col,Target_Pixel_Bot}) end if end if end if Pixel_Bot_Exec_Call_Id_List[Target_Pixel_Bot] = Limbo end for position(1,2) puts(1,"The winner is : " & Pixel_Bot_Name_List[Winning_Pixel_Bot]) while get_key() < 0 do end while Finished = TRUE end procedure procedure Last_Parent_Standing_Wins() for Target_Pixel_Bot = 1 to Pixel_Bot_Count do if Pixel_Bot_Exec_Call_Id_List[Target_Pixel_Bot] != Limbo then if Pixel_Bot_Clone_List[Target_Pixel_Bot] <= 0 then This_Pixel_Bot_Wins(Target_Pixel_Bot) end if end if end for end procedure procedure Cause_Collision_Damage() --// INCOMPLETE end procedure -- The Pixel Bot has tried to make a Move_Forward() but has collided with -- something; another Pixel Bot or another object ( such as a wall ). -- At present such collisions have no effect, however, collisions that do -- occur may cause damage or even destruction ( if a land mine ) in the -- future depending upon the type of scenario the arena program is -- running. -- If the arena program is running a maze solving program then there will -- need to be an exit point marked somewhere in the arena that has to be -- found. A call to here will occur when the Pixel Bot makes a deliberate -- attempt to step onto that exit point object and the arena program will -- need to be terminated with that Pixel Bot being decalred the winner. procedure Collision_Occured() -- Collided with something integer Hit Hit = Look(AHEAD) -- What did we hit ? if Hit = EXIT_POINT then -- Maze Exit if Pixel_Bot_Clone_List[This_Pixel_Bot] <= 0 then -- Parent This_Pixel_Bot_Wins(This_Pixel_Bot) else Cause_Collision_Damage() end if elsif Hit = TELEPORT then -- Teleport --// INCOMPLETE else -- Collison with object Cause_Collision_Damage() end if end procedure procedure Move_Relative(integer Inc_Row,integer Inc_Col) integer Row integer Col Row = Pixel_Bot_Row_List[This_Pixel_Bot] Col = Pixel_Bot_Col_List[This_Pixel_Bot] GUI(Move_Pixel_Bot_From,{Row,Col,This_Pixel_Bot}) The_World[Row][Col]=0 Row = Row + Inc_Row Col = Col + Inc_Col Pixel_Bot_Row_List[This_Pixel_Bot] = Row Pixel_Bot_Col_List[This_Pixel_Bot] = Col GUI(Make_Pixel_Bot_Face,{This_Pixel_Bot,Pixel_Bot_Facing_List[This_Pixel_Bot]}) GUI(Move_Pixel_Bot_To,{Row,Col,This_Pixel_Bot}) The_World[Row][Col]=This_Pixel_Bot end procedure global procedure Move_Forward() integer Direction if not Made_A_Move then -- Not already moved if not Damaged() then -- Not damaged if Blocked() then -- Blocked Collision_Occured() -- Collided with object else Direction = Facing() if Direction = NORTH then Move_Relative(-1,0) elsif Direction = SOUTH then Move_Relative(+1,0) elsif Direction = EAST then Move_Relative(0,+1) elsif Direction = WEST then Move_Relative(0,-1) end if Made_A_Move = TRUE -- Note that we moved end if end if end if end procedure global procedure Turn_Left() integer Direction if ( not Made_A_Move ) or ( not Made_A_Turn ) then Direction = Pixel_Bot_Facing_List[This_Pixel_Bot] if Direction = 0 then Direction = 3 else Direction = Direction - 1 end if Pixel_Bot_Facing_List[This_Pixel_Bot] = Direction Made_A_Turn = TRUE -- Note that we turned end if end procedure global procedure Turn_Right() integer Direction if ( not Made_A_Move ) or ( not Made_A_Turn ) then Direction = Pixel_Bot_Facing_List[This_Pixel_Bot] if Direction = 3 then Direction = 0 else Direction = Direction + 1 end if Pixel_Bot_Facing_List[This_Pixel_Bot] = Direction Made_A_Turn = TRUE -- Note that we turned end if end procedure -- *********************************************************************** -- * * -- * WEAPONS API ROUTINES * -- * * -- *********************************************************************** global function Attacked_By() return Pixel_Bot_Attacked_By_List[This_Pixel_Bot] end function global function Health() return floor( Pixel_Bot_Health_List[This_Pixel_Bot] ) end function global function Laser_Power() return floor(Pixel_Bot_Laser_Power_List[This_Pixel_Bot]) end function global procedure Fire_Laser() integer Target_Pixel_Bot atom Damage_Done if Laser_Power() > 0 then -- There is some power Target_Pixel_Bot = Look(AHEAD) -- Find the target if Target_Pixel_Bot > 0 then -- Is another Pixel Bot Damage_Done = Laser_Power() Pixel_Bot_Attacked_By_List[Target_Pixel_Bot] = This_Pixel_Bot Pixel_Bot_Health_List[Target_Pixel_Bot] = Pixel_Bot_Health_List[Target_Pixel_Bot] - Damage_Done if Pixel_Bot_Health_List[Target_Pixel_Bot] <= 25.0 then if Pixel_Bot_Health_List[Target_Pixel_Bot] < 0.0 then Pixel_Bot_Health_List[Target_Pixel_Bot] = 0.0 end if GUI( Damage_Pixel_Bot_At, { Pixel_Bot_Row_List[Target_Pixel_Bot], Pixel_Bot_Col_List[Target_Pixel_Bot], Target_Pixel_Bot } ) end if end if Pixel_Bot_Laser_Power_List[This_Pixel_Bot] = 0.0 end if end procedure -- *********************************************************************** -- * * -- * COMMUNICATIONS API FUNCTIONS * -- * * -- *********************************************************************** global function Message_Count() return length( Pixel_Bot_Message_List[This_Pixel_Bot] ) end function global function Any_Message() return ( Message_Count() > 0 ) end function global procedure Ignore_From(integer From) sequence Listen integer Index if From < 0 then Pixel_Bot_Message_List[This_Pixel_Bot] = { } Listen = { } else Listen = Pixel_Bot_Listen_List[This_Pixel_Bot] if length( Listen ) != 0 then Index = find( From , Listen ) if Index > 0 then if length( Listen ) = 1 then Listen = {} else if Index = 1 then Listen = Listen[2..length(Listen)] else if Index = length( Listen ) then Listen = Listen[1..length(Listen)-1] else Listen = Listen[1..Index-1] & Listen[Index+1..length(Listen)] end if end if end if end if end if Pixel_Bot_Listen_List[This_Pixel_Bot] = Listen end if end procedure global procedure Listen_To(integer From) if From >= 0 and From <= Pixel_Bot_Count then if From != This_Pixel_Bot then if find( From , Pixel_Bot_Listen_List[This_Pixel_Bot] ) = 0 then Pixel_Bot_Listen_List[This_Pixel_Bot] = Pixel_Bot_Listen_List[This_Pixel_Bot] & From end if end if end if end procedure global function Read_Message() sequence Message if length( Pixel_Bot_Message_List[This_Pixel_Bot] ) = 0 then return { } else Message = Pixel_Bot_Message_List[This_Pixel_Bot][1] if length( Pixel_Bot_Message_List[This_Pixel_Bot] ) = 1 then Pixel_Bot_Message_List[This_Pixel_Bot] = { } else Pixel_Bot_Message_List[This_Pixel_Bot] = Pixel_Bot_Message_List[This_Pixel_Bot] [2..length(Pixel_Bot_Message_List[This_Pixel_Bot])] end if return Message end if end function -- Send a message to another Pixel Bot. We can only send one message per -- go. If we are sending to the global message channel we find all the -- Pixel Bot's that are isteneing to that channel and send the message to -- them. If the Pixel Bot we are sending to doesn't want to listen to us -- we don't send the message. global procedure Send_Message(integer To,sequence Message) if not Sent_A_Message then if To = 0 then for Target = 1 to Pixel_Bot_Count do if find( 0 , Pixel_Bot_Listen_List[Target] ) != 0 then Send_Message(Target,Message) end if end for else if To > 0 and To <= Pixel_Bot_Count then if find( This_Pixel_Bot , Pixel_Bot_Listen_List[To] ) != 0 then Pixel_Bot_Message_List[To] = append ( Pixel_Bot_Message_List[To] , To & This_Pixel_Bot & { Message } ) Sent_A_Message = TRUE end if end if end if end if end procedure -- *********************************************************************** -- * * -- * REGISTRATION API ROUTINES * -- * * -- *********************************************************************** global procedure Register_Init(integer id) Pixel_Bot_Count = Pixel_Bot_Count + 1 Parents_Alive_Count = Parents_Alive_Count + 1 Pixel_Bots_Alive_Count = Pixel_Bots_Alive_Count + 1 Pixel_Bot_Init_Call_Id_List = append( Pixel_Bot_Init_Call_Id_List , id ) Pixel_Bot_Exec_Call_Id_List = append( Pixel_Bot_Exec_Call_Id_List , 0 ) Pixel_Bot_Name_List = append( Pixel_Bot_Name_List , "" ) Pixel_Bot_Facing_List = append( Pixel_Bot_Facing_List , 0 ) Pixel_Bot_Health_List = append( Pixel_Bot_Health_List , 100.0 ) Pixel_Bot_Attacked_By_List = append( Pixel_Bot_Attacked_By_List , 0 ) Pixel_Bot_Laser_Power_List = append( Pixel_Bot_Laser_Power_List , 100.0 ) Pixel_Bot_Row_List = append( Pixel_Bot_Row_List , 0 ) Pixel_Bot_Col_List = append( Pixel_Bot_Col_List , 0 ) Pixel_Bot_Clone_List = append( Pixel_Bot_Clone_List , 0 ) Pixel_Bot_Message_List = append( Pixel_Bot_Message_List , {} ) Pixel_Bot_Listen_List = append( Pixel_Bot_Listen_List , {} ) Run_Order_List = append( Run_Order_List, Pixel_Bot_Count ) end procedure global procedure Register_Exec(integer id) Pixel_Bot_Exec_Call_Id_List[Pixel_Bot_Count] = id end procedure global procedure Register_Name(sequence name) Pixel_Bot_Name_List[Pixel_Bot_Count] = name end procedure global procedure Register_Vers(atom Version) Version_Control(PIXEL_BOT_VERSIONS,Version,"Pixel Bot Program ( " & Pixel_Bot_Name_List[Pixel_Bot_Count] & " )" ) end procedure -- A Pixel Bot can only be cloned once and cloned Pixel Bots cannot -- create clones of themselves. -- We need to adjust Paren_Count for clones because these are really -- parents although the Register_Init() will have counted them as -- such. global procedure Create_Clones(integer Clones) integer Init_Call_Id integer Exec_Call_Id sequence Name if Game_Maximum_Number_Of_Clones_Allowed != 0 then if Game_Maximum_Number_Of_Clones_Allowed > 0 then if Clones > Game_Maximum_Number_Of_Clones_Allowed then Clones = Game_Maximum_Number_Of_Clones_Allowed end if end if if Clones >= 1 then if Pixel_Bot_Clone_List[Pixel_Bot_Count] = 0 then -- not cloned Init_Call_Id = Pixel_Bot_Init_Call_Id_List[Pixel_Bot_Count] Exec_Call_Id = Pixel_Bot_Exec_Call_Id_List[Pixel_Bot_Count] Name = Pixel_Bot_Name_List[Pixel_Bot_Count] Pixel_Bot_Name_List[Pixel_Bot_Count] = Name & " #0" Pixel_Bot_Clone_List[Pixel_Bot_Count] = -1 for Clone = 1 to Clones do Register_Init(Init_Call_Id) Register_Exec(Exec_Call_Id) Register_Name(Name & " #" & sprintf("%d",Clone) ) Pixel_Bot_Clone_List[Pixel_Bot_Count] = Clone Parents_Alive_Count = Parents_Alive_Count - 1 -- Wasn't a parent end for end if end if end if end procedure -- The Clone_Number() function is used to return a number which will -- indicate which particular clone this Pixel Bot is if the parent Pixel -- Bot created clones using Create_Clone(). -- If this is a Pixel Bot that has not been cloned then the function will -- return zero. -- If this is a Pixel Bot that has been cloned then the function will -- return a value that indicates which clone it is; 1 to 'n' where a -- Create_Clones(n) call was made to create the clones. A value of zero -- is returned if this is the parent of the clones. -- The returned value may be used in a subsequent Kill_Clone() call to -- kill off the clone ( or parent if the value is zero ). -- If the Pixel Bot Program maintains seperate data arrays ( per clone ) -- then the sequences should be accessed using the returned value plus -- one as all Euphoria sequences use a value of 1 to indicate the first -- item in the sequence. global function Clone_Number() integer Clone Clone = Pixel_Bot_Clone_List[This_Pixel_Bot] if Clone < 0 then -- Is clone parent return 0 -- So return 0 else -- Single or Clone return Clone -- 0 = Parent end if end function -- This routine actually kills off a Pixel Bot if it has not already -- been killed of ( its execution routine is not Limbo ). -- Before we actually kill the Pixel Bot off we check to se if it is -- a parent and if so determine if there is only one other parent left -- in the arena. If this is the case and it is a battle to the death -- scenario we make the last one standing the winner. procedure Kill(integer Target_Pixel_Bot) integer Row integer Col if Pixel_Bot_Exec_Call_Id_List[Target_Pixel_Bot] != Limbo then Pixel_Bots_Alive_Count = Pixel_Bots_Alive_Count - 1 if Pixel_Bot_Clone_List[Target_Pixel_Bot] <=0 then Parents_Alive_Count = Parents_Alive_Count - 1 if Parents_Alive_Count = 1 then if not Game_Create_A_Maze_Exit then if not Finished then Last_Parent_Standing_Wins() end if end if end if end if Pixel_Bot_Exec_Call_Id_List[Target_Pixel_Bot] = Limbo -- Killed Pixel_Bot_Attacked_By_List[Target_Pixel_Bot] = 0 Pixel_Bot_Message_List[Target_Pixel_Bot] = {} -- Flush msgs Pixel_Bot_Listen_List[Target_Pixel_Bot] = {} -- Flush listen if Target_Pixel_Bot = This_Pixel_Bot then -- Killed self Made_A_Move = TRUE -- So can't move Sent_A_Message = TRUE -- Nor talk end if Row = Pixel_Bot_Row_List[Target_Pixel_Bot] Col = Pixel_Bot_Col_List[Target_Pixel_Bot] The_World[Row][Col] = NOTHING GUI(Destroy_Pixel_Bot_At,{Row,Col,Target_Pixel_Bot}) Too_Slow = FALSE -- May be faster now end if end procedure -- If this is a parent Pixel Bot that has clones then it can kill -- off its created clones. If it created 99 clones the first is -- killed by Kill_Clone(1) the last by Kill_Clone(99); the number -- in Pixel_Bot_Clone_list[] is the same as the clone number a -- parent will have value -1 a standalone Pixel Bot that hasn't -- created any clones will have a value of zero. Any Pixel Bot -- can kill itself by using Kill_Clone(0). global procedure Kill_Clone(integer number) integer Target boolean Done if number = 0 then Kill(This_Pixel_Bot) else if Pixel_Bot_Clone_List[This_Pixel_Bot] <= 0 then -- parent Target = This_Pixel_Bot + 1 Done = FALSE while not Done do if Target > Pixel_Bot_Count then Done = TRUE else if Pixel_Bot_Clone_List[Target] <= 0 then Done = TRUE else if Pixel_Bot_Clone_List[Target] = number then Kill(Target) Done = TRUE else Target = Target + 1 end if end if end if end while end if end if end procedure -- Returns a string representing the name of a Pixel Bot or some -- other object in the arena. If the number is invalid then an -- empty string is returned. global function Get_Name(integer number) if number < 0 then -- A non-Pixel Bot if number = WALL then return "Wall" -- A wall elsif number = EXIT_POINT then return "Exit" -- An Exit else return "" -- Not valid object end if else if number = 0 then -- This Pixel Bot return Pixel_Bot_Name_List[This_Pixel_Bot] else -- A non-Pixel Bot if (number) > Pixel_Bot_Count then -- Not a valid Pixel Bot return "" else return Pixel_Bot_Name_List[number] -- Pixel Bot's name end if end if end if end function -- *********************************************************************** -- * * -- * ROUTINES USED BY THE ARENA PROGRAM - INITIALIZE PIXEL BOTS * -- * * -- *********************************************************************** -- The first thing to do is to find an empty space on the arena in which -- to place each Pixel Bot. -- In this version; a Pixel Bot can be placed anywhere where there is not -- a wall or some other object; later versions ( especially those where a -- maze solving arena is used ) may place all Pixel Bots next to each -- other and well away from the exit point. -- The second thing to do is to let the Graphical Interface know that we -- have placed a Pixel Bot in the arena. -- To let the Graphical Interface allocate colours ( or Icons in a more -- advanced design ) we place all the different types of Pixel Bots in -- different groups; this is done using a Group number. -- All standalone Pixel Bots ( those that don't produce clones ) and -- Parent Pixel Bots ( those that produced clones ) are placed in their -- own distinct groups; the group number will always have a value that -- is even. -- All clones, from a particular parent, will be placed together in a -- group whose number is one greater than its parent; the group number -- will always have a value that is odd ( numerically speaking ). procedure Place_This_Pixel_Bot_In_The_World(integer This_Pixel_Bot) sequence Place integer Row integer Col Place = Find_Place( Pixel_Bot_Places ) Row = Place[1] Col = Place[2] The_World[Row][Col] = This_Pixel_Bot Pixel_Bot_Row_List[This_Pixel_Bot] = Row Pixel_Bot_Col_List[This_Pixel_Bot] = Col if Pixel_Bot_Clone_List[This_Pixel_Bot] <= 0 then -- Parent Group = Group + 1 if remainder(Group,2) = 1 then Group = Group + 1 end if else if Pixel_Bot_Clone_List[This_Pixel_Bot] = 1 then -- First Cloned Group = Group + 1 end if end if GUI(Create_Pixel_Bot,{Row,Col,This_Pixel_Bot,Group}) end procedure procedure Pre_Initialize_Pixel_Bots() -- for PixelBot = 1 to Pixel_Bot_Count do -- puts(1, "Initializing : " & Pixel_Bot_Name_List[PixelBot] & "\n") -- end for end procedure procedure Initialize_Pixel_Bots() for PixelBot = 1 to Pixel_Bot_Count do This_Pixel_Bot = PixelBot Pixel_Bot_Facing_List[PixelBot] = Random(0,3) Pixel_Bot_Laser_Power_List[PixelBot] = 100.0 Pixel_Bot_Attacked_By_List[PixelBot] = 0 Pixel_Bot_Health_List[PixelBot] = 0.0 Place_This_Pixel_Bot_In_The_World(PixelBot) Made_A_Move = TRUE -- no moves allowed Made_A_Turn = FALSE -- but can turn Sent_A_Message = FALSE -- can't send msg call_proc(Pixel_Bot_Init_Call_Id_List[PixelBot],{}) end for end procedure procedure Post_Initialize_Pixel_Bots() for PixelBot = 1 to Pixel_Bot_Count do Pixel_Bot_Attacked_By_List[PixelBot] = 0 Pixel_Bot_Health_List[PixelBot] = 100.0 This_Pixel_Bot = PixelBot end for end procedure -- *********************************************************************** -- * * -- * ROUTINES USED BY THE ARENA PROGRAM - EXECUTE PIXEL BOTS * -- * * -- *********************************************************************** -- The In_limbo() routine is never ( normally ) executed. It exists so -- that the Limbo variable can have a unique value that will not be same -- as any Pixel Bot's execution procedure. This means that the execution -- procedure registered by a Pixel Bot can be replaced ( and checked for ) -- so that dead pixel Bots don't do anything. If the Pixel Bot does get -- a call to its execution routine; it wll call here and nothing will -- happen, but, it doesn't cause a crash either. procedure In_Limbo() -- Do Nothing ! end procedure Limbo = routine_id("In_Limbo") -- Mark dead Bots -- This routine determines the order of execution of the Pixel Bots at -- the start of each system 'tick'. If the time taken to handle the -- execution of all Pixel Bots is getting too long ( Too_Slow = TRUE ); -- we don't bother trying to re-order as this will just increase the -- time further. -- If we are re-ordering; one Pixel Bot is chosen at random and is -- swapped with the one at the top of the running list. Only one swap -- is done per tick but over a period of time the order should be very -- different to when it started. -- The running order list is sorted so that any Pixel Bots that were -- attacked during the last tick are given a chance to run first so -- they can take avoiding action or return fire before the attacker -- gets out the way. procedure Determine_Order_Of_Execution() integer r integer temp integer put_ptr if not Too_Slow then -- If we have time ... r = Random(1,Pixel_Bot_Count) -- Find one to put at top temp = Run_Order_List[1] -- Swap the top and chosen Run_Order_List[1]=Run_Order_List[r] Run_Order_List[r]=temp -- Put attacked at top put_ptr = 1 for i = 1 to Pixel_Bot_Count do if Pixel_Bot_Attacked_By_List[Run_Order_List[i]] != 0 then temp = Run_Order_List[put_ptr] Run_Order_List[put_ptr]=Run_Order_List[i] Run_Order_List[i]=temp put_ptr = put_ptr + 1 end if end for end if end procedure -- Every tick we charge up the laser until it is at 100% charge. procedure Charge_Up_Laser_Power() if Laser_Power() < 100 then Pixel_Bot_Laser_Power_List[This_Pixel_Bot] = Pixel_Bot_Laser_Power_List[This_Pixel_Bot] + INCREASE_LASER_PER_TICK end if end procedure procedure Increase_Health() if Pixel_Bot_Health_List[This_Pixel_Bot] < 100.0 then Pixel_Bot_Health_List[This_Pixel_Bot] = Pixel_Bot_Health_List[This_Pixel_Bot] + INCREASE_HEALTH_PER_TICK if Pixel_Bot_Health_List[This_Pixel_Bot] > 100.0 then Pixel_Bot_Health_List[This_Pixel_Bot] = 100.0 end if if not Damaged() then GUI(Move_Pixel_Bot_To, { Pixel_Bot_Row_List[This_Pixel_Bot], Pixel_Bot_Col_List[This_Pixel_Bot], This_Pixel_Bot } ) end if end if end procedure -- We execute each Pixel Bot ( if not dead ) one at a time. We -- charge up its laser and increase its health and then let it -- do its stuff. procedure Execute_Pixel_Bots() GUI(Synchronise_Front_End,{}) for PixelBot = 1 to Pixel_Bot_Count do This_Pixel_Bot = Run_Order_List[PixelBot] if Pixel_Bot_Exec_Call_Id_List[This_Pixel_Bot] != Limbo then Made_A_Move = FALSE -- can make move Made_A_Turn = FALSE -- can make turn Sent_A_Message = FALSE -- can send msg Charge_Up_Laser_Power() Increase_Health() call_proc(Pixel_Bot_Exec_Call_Id_List[This_Pixel_Bot],{}) Pixel_Bot_Attacked_By_List[This_Pixel_Bot] = 0 GUI(Kick_Front_End,{}) end if end for end procedure -- *********************************************************************** -- * * -- * THE ARENA PROGRAM * -- * * -- *********************************************************************** procedure Introduction() Welcome( Read_Options_From_Disk , { "ARENA.CFG" } ) Welcome( Show_Welcome_Screen , {} ) Welcome( Change_Options , {} ) Welcome( Write_Options_To_Disk , { "ARENA.CFG" } ) end procedure procedure Get_Options() integer dummy Game_Create_A_Maze = Welcome_Return(Option_Setting,{ "CREATE MAZE" } ) Game_Create_A_Maze_Exit = Welcome_Return(Option_Setting,{ "CREATE MAZE EXIT" } ) dummy = Welcome_Return(Option_Setting,{ "DEATH MATCH" } ) dummy = Welcome_Return(Option_Setting,{ "WEAPONS SELF-DESTRUCT" } ) dummy = Welcome_Return(Option_Setting,{ "WEAPONS ARE ALLOWED" } ) dummy = Welcome_Return(Option_Setting,{ "PIXEL BOTS CAN DIE" } ) Game_Maximum_Number_Of_Clones_Allowed= Welcome_Return(Option_Setting, { "MAXIMUM CLONES ALLOWED" } ) end procedure procedure Initialize() machine_proc(42,FALSE) -- Disable Ctrl-Break Pre_Initialize_Pixel_Bots() Create_The_World() Initialize_Pixel_Bots() Post_Initialize_Pixel_Bots() Finished = FALSE Too_Slow = FALSE end procedure procedure Run() atom Timeout Execute_Pixel_Bots() Timeout = time() + Tick_Time while not Finished do Determine_Order_Of_Execution() if time() < Timeout then while time() < Timeout do end while else Too_Slow = TRUE end if Timeout = time() + Tick_Time Execute_Pixel_Bots() if get_key() >= 0 then Finished = TRUE end if end while end procedure -- When everything is completed we should exit the program; the most -- important thing to do is to make sure that we come out of graphics -- mode and back into the traditional text mode. procedure Exit() GUI(Leave_Graphics_Mode,{}) end procedure -- The traditional 'text book' program; initialize the data, run the -- program then exit. procedure Run_The_Arena_Program() Initialize() Run() Exit() end procedure -- *********************************************************************** -- * * -- * INITIALIZATION AND CONFIGURATION * -- * * -- *********************************************************************** -- These need to be run before execute the registration routines in the -- Pixel Bot definitions as the options selected can affect what we do -- with each Pixel Bot as it is registered. Link_To_The_Front_End() Link_To_Welcome() Introduction() Get_Options() -- *********************************************************************** -- * * -- * USER DEFINED PIXEL BOTS * -- * * -- *********************************************************************** -- Include all the user defined Pixel Bots. Note that it is not possible -- to use more than one of the same Pixel Bot programs unless they exist -- as two seperate files; the contents may however be exactly the same or -- the Clone_This_Pixel_Bot() procedure may be called after the Pixel Bot -- has completed its registration. include pixelbot.e -- *********************************************************************** -- * * -- * RUN THE ARENA PROGRAM * -- * * -- *********************************************************************** Run_The_Arena_Program() -- Make everything happen -- *********************************************************************** -- * * -- * END OF THE ARENA PROGRAM * -- * * -- ***********************************************************************