Coin Toss Graphics Program - Magdha - 11-23-2025
I created this program to demonstrate the wide applicability of the QB64 _MAPTRIANGLE procedure for creating impressive graphics applications. _MAPTRIANGLE, most especially _MAPTRIANGLE(3D), is another amazing and supremely useful QB64 coding technique, and can be easily implemented by the novice/low-skill coder - I include myself very much within this group: OpenGL is there for the higher-skilled coders.
This looked to be a good project with the goal of simulating a 50-50 Heads-Tails toss of a spinning coin. To simulate a coin-tossing, the basic starting points were:
- A thin cylindrical 'solid' body with two different faces.
- The body describes a 3D path where it moves under a constant downward gravitational acceleration.
- In travelling such a path, the body spins upon an axis through a fixed diagonal.
- Upon hitting a solid flat surface the coin settles to lay flat - which way it falls determined by the angle at which it hit that surface.
- A degree of randomness in the flight so that there is a 50-50 chance of falling on each face.
To give a good representation of a coin shape in 3D, the Web was scanned for attractive images of the two faces of a coin in flat right-angle view. The milled edge was created in code to give cyclic lighter-darker regions (which looks pretty realistic).
The flat faces are mapped into the _MAPTRIANGLE(3D) space by cutting the faces into radial triangular segments. Easily done - this is the beauty of _MAPTRIANGLE(3D).
The motion of the coin in the 3D space was easy to code. The starting assumptions were:
- The coin travels in a straight line forward (the x- axis of the _MAPTRIANGLE(3D) space) with constant speed.
- After being launched from the starting position, the coin spins at a constant rotation.
- The initial upward (_MAPTRIANGLE(3D) space y-axis) varies at each toss. This sets the (random) time of flight.
The _MAPTRIANGLE(3D) view is rotated in the y-axis (vertical) to give a perspective view of the coin travel.
Getting the equations correct for the coin rotation and the horizontal and vertical motions was a doddle, of course. The complicated part was in working out the appropriate equations for the landing of the coin and its subsequent fall to the landing surface. The coin is going to land on one of four edge corners (front and rear faces meeting the edge 'up' and 'down'). In fact this is the simplest of trigonometry, working out triangle geometry, but it took me ten times longer doing this to get it correct for all conditions than it did to code the graphics - again this demonstrates the user-friendliness of _MAPTRIANGLE(3D).
The first video shows the running program with consecutive tails and heads. The graphics are pretty convincing, with realistic coin rotation, movement, landing and settling. When the coin has landed it falls to the nearest horizontal angle. The coin has no inertial mass even though it has gravitational mass. The coin rotation rate (and overall speed) is a compromise between showing the full movement and the time taken for a tossing event. With a faster display speed the spinning would hardly be noticeable, but even so a single event takes much longer than a real coin would. Even so, the rotation is rather difficult to appreciate and even appears unnatural at times (somewhat distorted by the perspective of the 3D space). The second video shows the process happening at a slowed-down rate and it is clear that the coin truly spins as in reality.
To run the program, download and extract the zip file. Place the folder in your QB64 directory.
Flippin' Coin.zip (Size: 2.77 MB / Downloads: 20)
Press the Spacebar to initiate a toss. The coin will land either heads up or tails up (this you would expect!). Depending upon which of the four edge corners the coin lands on, it will either settle backwards as in the video (this is by far the usual case) or forwards. Esc to Quit.
Data gathering was performed using a calculation-only program (no graphics) over 2000 cycles and it was found that the heads-tails ratio was 50-50 to within 1% - pretty good!
Code: (Select All)
'Coin Toss Graphics Program by Magdha 2025-11-23 ex Qwerkey
'Demonstrates Simplicity and Effectiveness of _MAPTRIANGLE(3D)
CONST False = 0, True = NOT False
CONST CalcRate% = 400, DispRate% = 60, DispCount% = INT(CalcRate% / DispRate%), Phi! = _PI / 4
CONST NoSegs% = 100, Radius% = 100, Halfwidth% = 5, EdgeLess1% = 2 * Halfwidth% - 1, EdgingLess1% = 199
CONST Offset% = -400, Theta! = 2 * _PI / NoSegs%, Squash! = 0.8
CONST XStart% = 0, YStart% = -180, ZStart% = -400, YLand% = -220, Jeez! = 0.0001, EgadSir! = 0.04
CONST Zeta! = ATN(Halfwidth% / Radius%), EllsBells! = SQR(Halfwidth% * Halfwidth% + Radius% * Radius%)
CONST ScreenScale = 1.6 'Modification with Steve NcNeill's Display Scale
DIM FlippinEck!(1, NoSegs% - 1, 2, 2) ' Two faces, NoSegs%-1 sements, 3 positions, 3 co-ordinates
DIM SpinMeRound!(1, 2, 2) ' Two faces, 3 positions, 3 co-ordinates
DIM FarmerGeorge!(1, 2) 'Position/Velocity, 3 co-ordinates
DIM BaizeCorners!(2, 3, 2) 'Region, Corner, Co-ordinates
DIM BaizePosn%(2, 2) 'Plane, Co-ordinates
DIM MapFrom!(NoSegs% - 1, 7)
_TITLE "Inrequieto ambitu Georgius tertius - Esc to Quit"
'Load/Create Images
'Coin Faces
TempImg& = _LOADIMAGE("Obverse.png", 32) 'Obverse image is loaded backwards (rather oddly)
TempImg1& = _NEWIMAGE(2 * Radius% + 1, 2 * Radius% + 1, 32)
_DEST TempImg1&
_PUTIMAGE (2 * Radius%, 0)-(0, 2 * Radius%), TempImg&
Obverse& = _COPYIMAGE(TempImg1&, 33)
_FREEIMAGE TempImg1&
_FREEIMAGE TempImg&
TempImg& = _LOADIMAGE("Reverse.png", 32)
TempImg1& = _NEWIMAGE(2 * Radius% + 1, 2 * Radius% + 1, 32)
_DEST TempImg1&
_PUTIMAGE , TempImg&
Reverse& = _COPYIMAGE(TempImg1&, 33)
_FREEIMAGE TempImg1&
_FREEIMAGE TempImg&
TempImg& = _LOADIMAGE("Heads.png", 32)
TempImg1& = _NEWIMAGE(150, 150, 32)
_DEST TempImg1&
_PUTIMAGE , TempImg&
Heads& = _COPYIMAGE(TempImg1&, 33)
_FREEIMAGE TempImg1&
_FREEIMAGE TempImg&
TempImg& = _LOADIMAGE("Tails.png", 32)
TempImg1& = _NEWIMAGE(150, 150, 32)
_DEST TempImg1&
_PUTIMAGE , TempImg&
Tails& = _COPYIMAGE(TempImg1&, 33)
_FREEIMAGE TempImg1&
_FREEIMAGE TempImg&
'Coin Edge
TempImg& = _NEWIMAGE(EdgingLess1% + 1, EdgeLess1% + 1, 32)
_DEST TempImg&
FOR N% = 0 TO EdgingLess1%
T! = 0.8 * ((1 - (COS(N% * 2 * _PI / (EdgingLess1% + 1) / 2)) ^ 3) / 2) + 0.2
FOR M% = 0 TO 49
PSET (N%, M%), _RGB32(CINT(T! * 255), CINT(T! * 219), CINT(T! * 88))
NEXT M%
NEXT N%
EdgePart& = _COPYIMAGE(TempImg&, 33)
_FREEIMAGE TempImg&
'Plane Images
BaizeLoad& = _LOADIMAGE("baize.jpg", 32)
TempImg& = _NEWIMAGE(500, 500, 32)
_DEST TempImg&
_PUTIMAGE , BaizeLoad&
_FREEIMAGE BaizeLoad&
BaizeTopTempImg& = _COPYIMAGE(TempImg&, 32)
BaizeVertTempImg& = _COPYIMAGE(TempImg&, 32)
_DEST BaizeVertTempImg&
LINE (0, 0)-(499, 499), _RGBA32(0, 50, 0, 40), BF
BaizeVertImg& = _COPYIMAGE(BaizeVertTempImg&, 33)
_FREEIMAGE BaizeVertTempImg&
_DEST TempImg&
Fonty& = _LOADFONT("arial.ttf", 84)
_FONT Fonty&
COLOR _RGBA32(0, 50, 0, 20), _RGBA32(120, 120, 120, 0)
_PRINTSTRING (30, 320), "QWERKEY"
_FONT 16 'Use QB64 default font to free loaded font
_FREEFONT Fonty&
BaizeLandTempImg& = _NEWIMAGE(500, 500, 32)
_DEST BaizeLandTempImg&
_PUTIMAGE (499, 0)-(0, 499), TempImg&
_FREEIMAGE TempImg&
BaizeLandImg& = _COPYIMAGE(BaizeLandTempImg&, 33)
_FREEIMAGE BaizeLandTempImg&
' Ejector Could Be Added
BaizeTopImg& = _COPYIMAGE(BaizeTopTempImg&, 33)
_FREEIMAGE BaizeTopTempImg&
' Banners
TempImg& = _NEWIMAGE(210, 100, 32)
_DEST TempImg&
COLOR _RGB32(127, 127, 238), _RGB32(100, 100, 100)
CLS
Fonty& = _LOADFONT("arial.ttf", 30)
_FONT Fonty&
_PRINTSTRING (42, 15), "Spacebar"
_PRINTSTRING (40, 55), "to Restart"
LINE (0, 0)-(209, 99), _RGB32(250, 250, 250), B
LINE (1, 1)-(208, 98), _RGB32(250, 250, 250), B
_FONT 16
_FREEFONT Fonty&
RestartImg& = _COPYIMAGE(TempImg&, 33)
_FREEIMAGE TempImg&
TempImg& = _NEWIMAGE(210, 100, 32)
_DEST TempImg&
COLOR _RGB32(238, 238, 160), _RGB32(67, 127, 172)
CLS
Fonty& = _LOADFONT("arial.ttf", 30)
_FONT Fonty&
_PRINTSTRING (42, 15), "Spacebar"
_PRINTSTRING (60, 55), "to Start"
LINE (0, 0)-(209, 99), _RGB32(250, 250, 250), B
LINE (1, 1)-(208, 98), _RGB32(250, 250, 250), B
_FONT 16
_FREEFONT Fonty&
LaunchImg& = _COPYIMAGE(TempImg&, 33)
_FREEIMAGE TempImg&
'Define Positions of segments on front & back
FOR N% = 0 TO NoSegs% - 1
NTheta! = N% * Theta!
NPlusTheta! = NTheta! + Theta!
FlippinEck!(0, N%, 0, 0) = 0
FlippinEck!(0, N%, 0, 1) = 5
FlippinEck!(0, N%, 0, 2) = 0
FlippinEck!(0, N%, 1, 0) = Radius% * COS(NTheta!)
FlippinEck!(0, N%, 1, 1) = 5
FlippinEck!(0, N%, 1, 2) = -Radius% * SIN(NTheta!)
FlippinEck!(0, N%, 2, 0) = Radius% * COS(NPlusTheta!)
FlippinEck!(0, N%, 2, 1) = 5
FlippinEck!(0, N%, 2, 2) = -Radius% * SIN(NPlusTheta!)
FlippinEck!(1, N%, 0, 0) = 0
FlippinEck!(1, N%, 0, 1) = -5
FlippinEck!(1, N%, 0, 2) = 0
FlippinEck!(1, N%, 1, 0) = Radius% * COS(NTheta!)
FlippinEck!(1, N%, 1, 1) = -5
FlippinEck!(1, N%, 1, 2) = -Radius% * SIN(NTheta!)
FlippinEck!(1, N%, 2, 0) = Radius% * COS(NPlusTheta!)
FlippinEck!(1, N%, 2, 1) = -5
FlippinEck!(1, N%, 2, 2) = -Radius% * SIN(NPlusTheta!)
N1Theta! = NTheta! + Theta!
NThetaPi! = NTheta! - _PI
N1ThetaPi! = N1Theta! - _PI
MapFrom!(N%, 0) = Radius% * (1 + COS(NThetaPi!))
MapFrom!(N%, 1) = Radius% * (1 + SIN(NThetaPi!))
MapFrom!(N%, 2) = Radius% * (1 + COS(N1ThetaPi!))
MapFrom!(N%, 3) = Radius% * (1 + SIN(N1ThetaPi!))
MapFrom!(N%, 4) = Radius% * (1 + COS(NTheta!))
MapFrom!(N%, 5) = Radius% * (1 + SIN(NTheta!))
MapFrom!(N%, 6) = Radius% * (1 + COS(N1Theta!))
MapFrom!(N%, 7) = Radius% * (1 + SIN(N1Theta!))
NEXT N%
RANDOMIZE (TIMER)
'Initial Conditions
Pixar%% = False
Count% = 0
EezaTosser%% = True
Active%% = False
'Coin Parameters
FarmerGeorge!(0, 0) = XStart% 'Xposition
FarmerGeorge!(0, 1) = YStart% 'Yposition
FarmerGeorge!(0, 2) = ZStart% 'Zposition
FarmerGeorge!(1, 1) = 0.31 + RND * 0.04 '0.3125 'Yvelocity
FarmerGeorge!(1, 2) = 0.07 'Zvelocity
Psi! = 0 ' NB Have heads up at start of every spin (whether heads or tails previous)
SpinState%% = 0 '0 Start waiting, 1 Launch, 2 Motion, 3 Land/settle, 4 At land display Heads or Tails, 5 Return waiting, 6 Move back to start
'Base Parameters
BaizePosn%(0, 0) = -310
BaizePosn%(0, 1) = YStart% - Halfwidth% - 1
BaizePosn%(0, 2) = -250
BaizePosn%(1, 0) = 310
BaizePosn%(1, 1) = YLand% - Halfwidth% - 1
BaizePosn%(1, 2) = 400
BaizePosn%(2, 2) = -1000
BaizeCorners!(0, 0, 2) = BaizePosn%(0, 2) * COS(Phi!) - BaizePosn%(0, 0) * SIN(Phi!)
BaizeCorners!(0, 0, 0) = BaizePosn%(0, 2) * SIN(Phi!) + BaizePosn%(0, 0) * COS(Phi!)
BaizeCorners!(0, 1, 2) = BaizePosn%(0, 2) * COS(Phi!) - BaizePosn%(1, 0) * SIN(Phi!)
BaizeCorners!(0, 1, 0) = BaizePosn%(0, 2) * SIN(Phi!) + BaizePosn%(1, 0) * COS(Phi!)
BaizeCorners!(0, 2, 2) = BaizePosn%(2, 2) * COS(Phi!) - BaizePosn%(1, 0) * SIN(Phi!)
BaizeCorners!(0, 2, 0) = BaizePosn%(2, 2) * SIN(Phi!) + BaizePosn%(1, 0) * COS(Phi!)
BaizeCorners!(0, 3, 2) = BaizePosn%(2, 2) * COS(Phi!) - BaizePosn%(0, 0) * SIN(Phi!)
BaizeCorners!(0, 3, 0) = BaizePosn%(2, 2) * SIN(Phi!) + BaizePosn%(0, 0) * COS(Phi!)
BaizeCorners!(0, 0, 1) = BaizePosn%(0, 1)
BaizeCorners!(0, 1, 1) = BaizePosn%(0, 1)
BaizeCorners!(0, 2, 1) = BaizePosn%(0, 1)
BaizeCorners!(0, 3, 1) = BaizePosn%(0, 1)
BaizeCorners!(1, 0, 2) = BaizePosn%(0, 2) * COS(Phi!) - BaizePosn%(0, 0) * SIN(Phi!)
BaizeCorners!(1, 0, 0) = BaizePosn%(0, 2) * SIN(Phi!) + BaizePosn%(0, 0) * COS(Phi!)
BaizeCorners!(1, 1, 2) = BaizePosn%(0, 2) * COS(Phi!) - BaizePosn%(1, 0) * SIN(Phi!)
BaizeCorners!(1, 1, 0) = BaizePosn%(0, 2) * SIN(Phi!) + BaizePosn%(1, 0) * COS(Phi!)
BaizeCorners!(1, 2, 2) = BaizePosn%(0, 2) * COS(Phi!) - BaizePosn%(1, 0) * SIN(Phi!)
BaizeCorners!(1, 2, 0) = BaizePosn%(0, 2) * SIN(Phi!) + BaizePosn%(1, 0) * COS(Phi!)
BaizeCorners!(1, 3, 2) = BaizePosn%(0, 2) * COS(Phi!) - BaizePosn%(0, 0) * SIN(Phi!)
BaizeCorners!(1, 3, 0) = BaizePosn%(0, 2) * SIN(Phi!) + BaizePosn%(0, 0) * COS(Phi!)
BaizeCorners!(1, 0, 1) = BaizePosn%(0, 1)
BaizeCorners!(1, 1, 1) = BaizePosn%(0, 1)
BaizeCorners!(1, 2, 1) = BaizePosn%(1, 1)
BaizeCorners!(1, 3, 1) = BaizePosn%(1, 1)
BaizeCorners!(2, 0, 2) = BaizePosn%(1, 2) * COS(Phi!) - BaizePosn%(0, 0) * SIN(Phi!)
BaizeCorners!(2, 0, 0) = BaizePosn%(1, 2) * SIN(Phi!) + BaizePosn%(0, 0) * COS(Phi!)
BaizeCorners!(2, 1, 2) = BaizePosn%(1, 2) * COS(Phi!) - BaizePosn%(1, 0) * SIN(Phi!)
BaizeCorners!(2, 1, 0) = BaizePosn%(1, 2) * SIN(Phi!) + BaizePosn%(1, 0) * COS(Phi!)
BaizeCorners!(2, 2, 2) = BaizePosn%(0, 2) * COS(Phi!) - BaizePosn%(1, 0) * SIN(Phi!)
BaizeCorners!(2, 2, 0) = BaizePosn%(0, 2) * SIN(Phi!) + BaizePosn%(1, 0) * COS(Phi!)
BaizeCorners!(2, 3, 2) = BaizePosn%(0, 2) * COS(Phi!) - BaizePosn%(0, 0) * SIN(Phi!)
BaizeCorners!(2, 3, 0) = BaizePosn%(0, 2) * SIN(Phi!) + BaizePosn%(0, 0) * COS(Phi!)
BaizeCorners!(2, 0, 1) = BaizePosn%(1, 1)
BaizeCorners!(2, 1, 1) = BaizePosn%(1, 1)
BaizeCorners!(2, 2, 1) = BaizePosn%(1, 1)
BaizeCorners!(2, 3, 1) = BaizePosn%(1, 1)
SCREEN _NEWIMAGE(590, 630, 32)
_SCREENMOVE 100, 20
_DEST 0
'Default Display Order
WHILE EezaTosser%%
IF Active%% THEN
_LIMIT CalcRate%
ELSE
_LIMIT DispRate%
END IF
IF Pixar%% THEN
FOR N% = 0 TO NoSegs% - 1
'Coin angle in x-axis (Spin if Psi! changes) in original 3D space:
FOR F%% = 0 TO 1
FOR P%% = 0 TO 2
SpinMeRound!(F%%, P%%, 1) = FlippinEck!(F%%, N%, P%%, 1) * COS(Psi!) - FlippinEck!(F%%, N%, P%%, 2) * SIN(Psi!)
SpinMeRound!(F%%, P%%, 2) = FlippinEck!(F%%, N%, P%%, 1) * SIN(Psi!) + FlippinEck!(F%%, N%, P%%, 2) * COS(Psi!)
NEXT P%%
NEXT F%%
'Position in original 3D space:
FOR F%% = 0 TO 1
FOR P%% = 0 TO 2
'Following does not work - I do not see why
'FOR C%% = 0 TO 2
' SpinMeRound!(F%%, P%%, C%%) = FlippinEck!(F%%, N%, P%%, C%%) + FarmerGeorge!(0, C%%)
'NEXT C%%
SpinMeRound!(F%%, P%%, 0) = FlippinEck!(F%%, N%, P%%, 0) + FarmerGeorge!(0, 0)
SpinMeRound!(F%%, P%%, 1) = SpinMeRound!(F%%, P%%, 1) + FarmerGeorge!(0, 1)
SpinMeRound!(F%%, P%%, 2) = SpinMeRound!(F%%, P%%, 2) + FarmerGeorge!(0, 2)
NEXT P%%
NEXT F%%
'Rotate in y-axis (Tilt to display co-ordinates):
FOR F%% = 0 TO 1
FOR P%% = 0 TO 2
ZTemp! = SpinMeRound!(F%%, P%%, 2)
SpinMeRound!(F%%, P%%, 2) = ZTemp! * COS(Phi!) - SpinMeRound!(F%%, P%%, 0) * SIN(Phi!)
SpinMeRound!(F%%, P%%, 0) = ZTemp! * SIN(Phi!) + SpinMeRound!(F%%, P%%, 0) * COS(Phi!)
NEXT P%%
NEXT F%%
'Display
'Map Obverse, Reverse & Edge
'Obverse is rotated through 180deg
ZTemp! = SpinMeRound!(0, 1, 2) + Offset%
ZTemp1! = SpinMeRound!(1, 1, 2) + Offset%
ZTemp2! = SpinMeRound!(0, 2, 2) + Offset%
ZTemp3! = SpinMeRound!(1, 2, 2) + Offset%
'Coin Obverse & Reverse
_MAPTRIANGLE (Radius%, Radius%)-(MapFrom!(N%, 0), MapFrom!(N%, 1))-(MapFrom!(N%, 2), MapFrom!(N%, 3)), Obverse& TO(SpinMeRound!(0, 0, 0), SpinMeRound!(0, 0, 1), SpinMeRound!(0, 0, 2) + Offset%)-(SpinMeRound!(0, 1, 0), SpinMeRound!(0, 1, 1), ZTemp!)-(SpinMeRound!(0, 2, 0), SpinMeRound!(0, 2, 1), ZTemp2!)
_MAPTRIANGLE (Radius%, Radius%)-(MapFrom!(N%, 4), MapFrom!(N%, 5))-(MapFrom!(N%, 6), MapFrom!(N%, 7)), Reverse& TO(SpinMeRound!(1, 0, 0), SpinMeRound!(1, 0, 1), SpinMeRound!(1, 0, 2) + Offset%)-(SpinMeRound!(1, 1, 0), SpinMeRound!(1, 1, 1), ZTemp1!)-(SpinMeRound!(1, 2, 0), SpinMeRound!(1, 2, 1), ZTemp3!)
'Coin Edge
_MAPTRIANGLE (0, 0)-(0, EdgeLess1%)-(EdgingLess1%, EdgeLess1%), EdgePart& TO(SpinMeRound!(0, 1, 0), SpinMeRound!(0, 1, 1), ZTemp!)-(SpinMeRound!(1, 1, 0), SpinMeRound!(1, 1, 1), ZTemp1!)-(SpinMeRound!(1, 2, 0), SpinMeRound!(1, 2, 1), ZTemp3!)
_MAPTRIANGLE (0, 0)-(EdgingLess1%, 0)-(EdgingLess1%, EdgeLess1%), EdgePart& TO(SpinMeRound!(0, 1, 0), SpinMeRound!(0, 1, 1), ZTemp!)-(SpinMeRound!(0, 2, 0), SpinMeRound!(0, 2, 1), ZTemp2!)-(SpinMeRound!(1, 2, 0), SpinMeRound!(1, 2, 1), ZTemp3!)
NEXT N%
'Baize Sections
'Start Plane
_MAPTRIANGLE (0, 0)-(499, 0)-(499, 499), BaizeTopImg& TO(BaizeCorners!(0, 0, 0), BaizeCorners!(0, 0, 1), BaizeCorners!(0, 0, 2) + Offset%)-(BaizeCorners!(0, 1, 0), BaizeCorners!(0, 1, 1), BaizeCorners!(0, 1, 2) + Offset%)-(BaizeCorners!(0, 2, 0), BaizeCorners!(0, 2, 1), BaizeCorners!(0, 2, 2) + Offset%)
_MAPTRIANGLE (0, 0)-(0, 499)-(499, 499), BaizeTopImg& TO(BaizeCorners!(0, 0, 0), BaizeCorners!(0, 0, 1), BaizeCorners!(0, 0, 2) + Offset%)-(BaizeCorners!(0, 3, 0), BaizeCorners!(0, 3, 1), BaizeCorners!(0, 3, 2) + Offset%)-(BaizeCorners!(0, 2, 0), BaizeCorners!(0, 2, 1), BaizeCorners!(0, 2, 2) + Offset%)
'Vertical Plane
_MAPTRIANGLE (0, 0)-(499, 0)-(499, 499), BaizeVertImg& TO(BaizeCorners!(1, 0, 0), BaizeCorners!(1, 0, 1), BaizeCorners!(1, 0, 2) + Offset%)-(BaizeCorners!(1, 1, 0), BaizeCorners!(1, 1, 1), BaizeCorners!(1, 1, 2) + Offset%)-(BaizeCorners!(1, 2, 0), BaizeCorners!(1, 2, 1), BaizeCorners!(1, 2, 2) + Offset%)
_MAPTRIANGLE (0, 0)-(0, 499)-(499, 499), BaizeVertImg& TO(BaizeCorners!(1, 0, 0), BaizeCorners!(1, 0, 1), BaizeCorners!(1, 0, 2) + Offset%)-(BaizeCorners!(1, 3, 0), BaizeCorners!(1, 3, 1), BaizeCorners!(1, 3, 2) + Offset%)-(BaizeCorners!(1, 2, 0), BaizeCorners!(1, 2, 1), BaizeCorners!(1, 2, 2) + Offset%)
'Landing Plane
_MAPTRIANGLE (0, 0)-(499, 0)-(499, 499), BaizeLandImg& TO(BaizeCorners!(2, 0, 0), BaizeCorners!(2, 0, 1), BaizeCorners!(2, 0, 2) + Offset%)-(BaizeCorners!(2, 1, 0), BaizeCorners!(2, 1, 1), BaizeCorners!(2, 1, 2) + Offset%)-(BaizeCorners!(2, 2, 0), BaizeCorners!(2, 2, 1), BaizeCorners!(2, 2, 2) + Offset%)
_MAPTRIANGLE (0, 0)-(0, 499)-(499, 499), BaizeLandImg& TO(BaizeCorners!(2, 0, 0), BaizeCorners!(2, 0, 1), BaizeCorners!(2, 0, 2) + Offset%)-(BaizeCorners!(2, 3, 0), BaizeCorners!(2, 3, 1), BaizeCorners!(2, 3, 2) + Offset%)-(BaizeCorners!(2, 2, 0), BaizeCorners!(2, 2, 1), BaizeCorners!(2, 2, 2) + Offset%)
'Heads or Tails (& Banner)
IF SpinState%% = 4 OR SpinState%% = 5 THEN
IF Psi! = 0 THEN
_PUTIMAGE (XTails%, 80), Heads&
ELSE
_PUTIMAGE (XTails%, 80), Tails&
END IF
IF SpinState%% = 5 THEN _PUTIMAGE (20, 80), RestartImg&
ELSEIF SpinState%% = 0 THEN
_PUTIMAGE (360, 510), LaunchImg&
END IF
'_DISPLAY
CALL DisplayScaled
Pixar%% = False
END IF
SELECT CASE SpinState%%
'CASE 0 'At Start Not required here
CASE 1 'Launch (move in original 3D space)
FarmerGeorge!(0, 2) = FarmerGeorge!(0, 2) + FarmerGeorge!(1, 2)
FarmerGeorge!(0, 1) = YStart! + 1 + Halfwidth% * COS(Psi!) + Radius% * SIN(Psi!)
Psi! = Psi! + EgadSir!
IF Psi! >= _PI / 2 THEN SpinState%% = 2
CASE 2 'Spin & move (move in original 3D space)
SELECT CASE Psi!
'There are 4 geometric positions to consider when the coin first touches landing area:
'Dependent upon Psi!
'The angles in the geometry are dependent upon which corner the coin lands on
'0 Tails, coin falls backwards
'1 Heads, coin falls backwards
'2 Tails, coin falls forwards
'3 Heads, coin falls forwards
CASE -_PI TO -0.5 * _PI
'Most Tails
Beta! = -(0.5 * _PI + Psi! + Zeta!)
Settle%% = 0
CASE 0 TO 0.5 * _PI
'Most Heads
Beta! = 0.5 * _PI - Psi! - Zeta!
Settle%% = 1
CASE 0.5 * _PI TO _PI
'Rare Tails
Beta! = 0.5 * _PI - Psi! + Zeta!
Settle%% = 2
CASE ELSE '-Pi/2 to 0
'Rare Heads
Beta! = -0.5 * _PI - Psi! + Zeta!
Settle%% = 3
END SELECT
ZNought! = EllsBells! * SIN(Beta!) + FarmerGeorge!(0, 2)
IF FarmerGeorge!(0, 1) - EllsBells! * COS(Beta!) < YLand% + 1 THEN
SpinState%% = 3
Psi0! = Psi!
PsiCount% = 400
ELSE
FarmerGeorge!(0, 2) = FarmerGeorge!(0, 2) + FarmerGeorge!(1, 2)
FarmerGeorge!(0, 1) = FarmerGeorge!(0, 1) + FarmerGeorge!(1, 1)
FarmerGeorge!(1, 1) = FarmerGeorge!(1, 1) - Jeez!
'Rotate in x-axis (Spin) in original 3D space:
'Rotates "in the direction of travel"
Psi! = Psi! + EgadSir!
IF Psi! > _PI THEN Psi! = Psi! - 2 * _PI
END IF
CASE 3 'Land & Settle
SELECT CASE Settle%%
'The reverse of above
CASE 0
'Most Tails
Beta! = -(0.5 * _PI + Psi! + Zeta!)
CASE 1
'Most Heads
Beta! = 0.5 * _PI - Psi! - Zeta!
CASE 2
'Rare Tails
Beta! = 0.5 * _PI - Psi! + Zeta!
CASE 3
'Rare Heads
Beta! = -0.5 * _PI - Psi! + Zeta!
END SELECT
FarmerGeorge!(0, 2) = ZNought! - EllsBells! * SIN(Beta!)
FarmerGeorge!(0, 1) = YLand! + 1 + EllsBells! * COS(Beta!)
IF Psi0! <= _PI / 2 AND Psi0! >= -_PI / 2 THEN
'Heads
IF PsiCount% = 0 THEN
Psi! = 0 'For heads
SpinState%% = 4
ELSE
Psi! = Psi0! * PsiCount% / 400
PsiCount% = PsiCount% - 1
END IF
ELSE
'Tails
IF Psi0! < -_PI / 2 THEN Psi0! = Psi0! + 2 * _PI
IF PsiCount% = 0 THEN
Psi! = _PI 'For tails
SpinState%% = 4
ELSE
Psi! = _PI + ((Psi0! - _PI) * PsiCount% / 400)
PsiCount% = PsiCount% - 1
END IF
END IF
CASE 4 'Landed (Heads or Tails)
IF PsiCount% < 400 THEN
XTails% = 591 - INT(PsiCount% / 2)
PsiCount% = PsiCount% + 1
ELSE
SpinState%% = 5
PsiCount% = 800
ZNought! = FarmerGeorge!(0, 2)
RHome! = (ZNought! - ZStart%) / 2
ZHome! = ZStart% + RHome!
Psi0! = Psi!
Active%% = False
END IF
' CASE 5 Not required here
CASE 6 'Move back to start
IF PsiCount% = 0 THEN
'At Start
FarmerGeorge!(0, 0) = XStart% 'Xposition
FarmerGeorge!(0, 1) = YStart% 'Yposition
FarmerGeorge!(0, 2) = ZStart% 'Zposition
FarmerGeorge!(1, 1) = 0.31 + RND * 0.04 'Yvelocity
Psi! = 0 'Heads up at start
SpinState%% = 0
Pixar%% = False
Count% = 0
Active%% = False
ELSEIF PsiCount% > 600 THEN
FarmerGeorge!(0, 1) = YLand% + 6 + (800 - PsiCount%) * (YStart% - (YLand% + 6)) / 200
PsiCount% = PsiCount% - 1
ELSE
ZStep! = (PsiCount% - 300) * RHome! / 300
IF ZStep! > RHome! THEN ZStep! = RHome!
FarmerGeorge!(0, 2) = ZHome! + ZStep!
FarmerGeorge!(0, 1) = YStart% + SQR(RHome! * RHome! - ZStep! * ZStep!) * Squash!
Psi! = Psi0! * PsiCount% / 600
PsiCount% = PsiCount% - 1
END IF
END SELECT
Crikey$ = INKEY$
IF Crikey$ <> "" THEN
IF ASC(Crikey$) = 27 THEN
EezaTosser%% = False
ELSEIF Crikey$ = " " THEN
SELECT CASE SpinState%%
CASE 0
SpinState%% = 1
Active%% = True
CASE 5
SpinState%% = 6
Active%% = True
END SELECT
END IF
END IF
Crikey$ = ""
Count% = Count% + 1
IF Count% = DispCount% THEN
Count% = 0
Pixar%% = True
END IF
WEND
SYSTEM
SUB DisplayScaled
STATIC DisplayDoubleScreen AS LONG
D = _DEST: S = _SOURCE
IF DisplayDoubleScreen = 0 THEN
DisplayDoubleScreen = _NEWIMAGE(_WIDTH * ScreenScale, _HEIGHT * ScreenScale, 32)
SCREEN DisplayDoubleScreen
END IF
_PUTIMAGE , S, DisplayDoubleScreen
_DISPLAY
_DEST D: _SOURCE S
END SUB
|