getKickPos field half (GoalKick home) = Just { zero & px = if (home == West) (penalty_area_depth - half_length) (half_length - penalty_area_depth) }
where
half_length = scale 0.5 field.flength
-getKickPos field half (Corner home edge) = Just { px = if (home == West && half == SecondHalf || home == East && half == FirstHalf)
+getKickPos field half (Corner home edge) = Just { px = if (home == East)
(half_radius_corner_kick_area - half_length)
(half_length - half_radius_corner_kick_area)
, py = if (edge == North)
half_width = scale 0.5 field.fwidth
half_length = scale 0.5 field.flength
half_radius_corner_kick_area = scale 0.5 radius_corner_kick_area
-getKickPos field half (Penalty home) = Just { zero & px = if (home == West && half == SecondHalf || home == East && half == FirstHalf)
+getKickPos field half (Penalty home) = Just { zero & px = if (home == East)
(penalty_spot_depth - half_length)
(half_length - penalty_spot_depth)
}
degree x = rad (toReal x * pi / 180.0)
normalize_radian :: !Real -> Real
-normalize_radian r
-| r < (~cycle) = normalize_radian (r + cycle)
-| r > cycle = normalize_radian (r - cycle)
-| otherwise = r
-where
- cycle = 2.0 * pi
+normalize_radian r = normalize r 100
+where
+ normalize r 0 = abort "Loop in normalize_radian. Mail p.achten@cs.ru.nl"
+ normalize r n
+ | r < ~pi = normalize (r + 2.0*pi) (n-1)
+ | r > pi = normalize (r - 2.0*pi) (n-1)
+ | otherwise = r
+
instance zero Angle where zero = Radian zero
instance + Angle where + (Radian r1) (Radian r2) = Radian (normalize_radian (r1 + r2))
px_bearing :: !base !target -> Angle | toPosition base & toPosition target
px_bearing base target
+| abs ratio > 1.0 = Radian zero // Corner-cases: d==0 and "large values of 1.0"
| v.dx >= zero && v.dy >= zero = Radian base_angle // 1st quadrant
| v.dx <= zero && v.dy >= zero = Radian (pi-base_angle) // 2nd quadrant
| v.dx <= zero && v.dy <= zero = Radian (base_angle-pi) // 3rd quadrant
ptarget = toPosition target
v = {dx = ptarget.px - pbase.px, dy = ptarget.py - pbase.py}
d = toReal (dist pbase ptarget)
- base_angle = acos ((toReal (abs v.dx)) / d)
+ ratio = toReal (abs v.dx) / d
+ base_angle = acos ratio
bearing :: !Angle !base !target -> Angle | toPosition base & toPosition target
bearing angle base target = px_bearing base target - angle
import StdEnvExt
import matchGame
import Umpire
+import NoReferee
// When coding for all referees, include following modules:
import RefereeCoach_Rounds_Assignment
import RefereeCoach_Slalom_Assignment
import RefereeCoach_Keeper_Assignment
allAvailableReferees :: [FootballField -> Referee]
-allAvailableReferees = [ umpire ]
+allAvailableReferees = [ umpire, NoReferee ]
// When coding for all referees, use following list:
++
[ RefereeCoach_Rounds
import Team_Student_DeepPass_Assignment
import Team_Student_Keeper_Assignment
import Team_MartLubbers
+import Team_Harmless
allAvailableTeams :: [Home FootballField -> Team]
allAvailableTeams = [ Team_MartLubbers
, Team_MiniEffies
+ , Harmless
, Team_Student_Rounds
, Team_Student_Slalom
, Team_Student_Passing
doSoccerFun :: !*World -> *World\r
doSoccerFun world = SoccerFunGUI2D world\r
\r
+simulationGranularity :: Seconds\r
+simulationGranularity = s 0.05\r
+\r
+// the original simulation expected to run at 0.1s; this function is used to adjust it to different rates\r
+rateAdjust :: (Real -> Real)\r
+rateAdjust = case simulationGranularity of\r
+ Seconds 0.1 -> id\r
+ Seconds 0.05 -> sqrt\r
+ Seconds x -> \y -> y^(10.0*x)\r
+\r
setMatchStart :: !Team !Team !FootballField !Referee !PlayingTime !RandomSeed -> Match\r
setMatchStart fstTeam sndTeam field referee time rs\r
= { team1 = validateTeam fstTeam\r
, theReferee = referee\r
, playingHalf = FirstHalf\r
, playingTime = time\r
- , unittime = s 0.05\r
+ , unittime = simulationGranularity\r
, score = (0,0)\r
, nextRandomP = nextRandomP\r
, seed = rs\r
angleDifficulty = angleHowFarFromPi (speed.direction-new_nose)\r
angleDifference = angleHowFarFromAngle speed.direction new_nose\r
new_stamina = alter_stamina ball fb angleDifficulty angleDifference\r
- new_vel = scale (1.4 * fb.health * new_stamina) (setbetween speed.velocity zero (maxVelocity (skillsAsList fb) angleDifficulty angleDifference))\r
+ new_vel = scale (fb.health * new_stamina) (setbetween speed.velocity zero (maxVelocity (skillsAsList fb) angleDifficulty angleDifference))\r
new_speed = {speed & velocity=new_vel}\r
new_position` = move_point (scale (dt * (toReal new_vel)) (toRVector new_speed.direction)) fb.pos\r
new_position = point_to_rectangle ({px=scale -0.5 flength, py=scale -0.5 fwidth},{px=scale 0.5 flength,py=scale 0.5 fwidth}) new_position` \r
where\r
old_height = ballPos.pz\r
in_the_air = old_height > zero\r
- resistance = if in_the_air air_resistance surface_resistance\r
+ resistance = rateAdjust if in_the_air air_resistance surface_resistance\r
dt = toReal unittime\r
surface_movement = scale (dt * (toReal v)) (toRVector d)\r
new_speed2D = let new_v = scale resistance v in {direction = d, velocity = if (new_v <= ms 0.05) zero new_v}\r
alter_stamina ballState fb angleDifficulty angleDifference\r
| velocity <= rfv // increase stamina\r
| stamina < MinimumFatigue = MinimumFatigue\r
- | otherwise = stamina^0.8 \r
-| otherwise = fatigue * factor\r
+ | otherwise = stamina^(rateAdjust 0.8)\r
+| otherwise = fatigue * (rateAdjust factor)\r
where\r
velocity = fb.speed.velocity\r
length = fb.length\r
stamina = fb.stamina\r
rfv = restore_stamina_velocity (ballIsGainedBy fb.playerID ballState) (skillsAsList fb) angleDifficulty angleDifference\r
diff = velocity - rfv\r
- fv = if (diff >= ms 6.0) (stamina^(stamina^(1.6 + 0.02 * toReal length)))\r
- (if (diff >= ms 4.0) (stamina^( 1.5 + 0.01 * toReal length))\r
- (if (diff >= ms 2.0) (stamina^( 1.4 - 0.01 * toReal length))\r
- (stamina^( 1.3 - 0.02 * toReal length))))\r
+ fv = if (diff >= ms 6.0) (stamina^rateAdjust (stamina^(1.6 + 0.02 * toReal length))) // problematic\r
+ (if (diff >= ms 4.0) (stamina^rateAdjust ( 1.5 + 0.01 * toReal length))\r
+ (if (diff >= ms 2.0) (stamina^rateAdjust ( 1.4 - 0.01 * toReal length))\r
+ (stamina^rateAdjust ( 1.3 - 0.02 * toReal length))))\r
factor = one - (toReal angleDifficulty)/(4.0*pi)\r
fatigue = if (stamina > MaximumFatigue) MaximumFatigue fv\r
\r
, ("&Predictable",Nothing,Nothing,noLS predictablef)\r
] 0 []\r
) []\r
- :+: SubMenu "Sp&eed" ( RadioMenu [ ("&Slowest",Nothing, Just '1', noLS (changeSpeedf timerId Slowest))\r
- , ("S&lower", Nothing, Just '2', noLS (changeSpeedf timerId Slower))\r
- , ("N&ormal", Nothing, Just '3', noLS (changeSpeedf timerId Normal))\r
+ :+: SubMenu "Sp&eed" ( RadioMenu [ ("&Slowest",Nothing, Just '`', noLS (changeSpeedf timerId Slowest))\r
+ , ("S&lower", Nothing, Just '1', noLS (changeSpeedf timerId Slower))\r
+ , ("N&ormal", Nothing, Just '2', noLS (changeSpeedf timerId Normal))\r
+ , ("F&ast", Nothing, Just '3', noLS (changeSpeedf timerId Fast))\r
, ("&Faster", Nothing, Just '4', noLS (changeSpeedf timerId Faster))\r
, ("Fas&test",Nothing, Just '5', noLS (changeSpeedf timerId Fastest))\r
] ([Slowest,Slower,Normal,Faster,Fastest]??options.displaySpeed+1) []\r
*/\r
stepMatchForGui :: !FootballGame !*env -> (![RefereeAction], !FootballGame, !*env) | FileSystem env\r
\r
-:: DisplaySpeed = Slowest | Slower | Normal | Faster | Fastest\r
+:: DisplaySpeed = Slowest | Slower | Normal | Fast | Faster | Fastest\r
instance toString DisplaySpeed\r
instance fromString DisplaySpeed\r
instance == DisplaySpeed\r
intervalFactor :: !DisplaySpeed -> Real\r
intervalFactor Slowest = 1.0 / 0.2 // five times slower\r
intervalFactor Slower = 1.0 / 0.5 // two times slower\r
-intervalFactor Normal = 1.0 // two times faster\r
+intervalFactor Normal = 1.0 // normal speed\r
+intervalFactor Fast = 1.0 / 2.0 // two times faster\r
intervalFactor Faster = 1.0 / 5.0 // five times faster\r
intervalFactor Fastest = zero // no timer delay\r
ReadableABC: False\r
ReuseUniqueNodes: True\r
Fusion: False\r
+ Module\r
+ Name: NoReferee\r
+ Dir: {Project}\StdReferee\r
+ Compiler\r
+ NeverMemoryProfile: False\r
+ NeverTimeProfile: False\r
+ StrictnessAnalysis: True\r
+ ListTypes: StrictExportTypes\r
+ ListAttributes: True\r
+ Warnings: True\r
+ Verbose: True\r
+ ReadableABC: False\r
+ ReuseUniqueNodes: True\r
+ Fusion: False\r
Module\r
Name: RefereeCoach_DeepPass_Assignment\r
Dir: {Project}\StdReferee\r
ReadableABC: False\r
ReuseUniqueNodes: True\r
Fusion: False\r
+ Module\r
+ Name: Team_Harmless\r
+ Dir: {Project}\StdTeam\r
+ Compiler\r
+ NeverMemoryProfile: False\r
+ NeverTimeProfile: False\r
+ StrictnessAnalysis: True\r
+ ListTypes: StrictExportTypes\r
+ ListAttributes: True\r
+ Warnings: True\r
+ Verbose: True\r
+ ReadableABC: False\r
+ ReuseUniqueNodes: True\r
+ Fusion: False\r
Module\r
Name: Team_Opponent_DeepPass_Assignment\r
Dir: {Project}\StdTeam\r
-{closeReferee=True,showSplash=False,displaySpeed=Faster,showReferee=True,playingTime=4:00 min,renderStyle=Fixed camera}
\ No newline at end of file
+{closeReferee=True,showSplash=False,displaySpeed=Normal,showReferee=True,playingTime=4:00 min,renderStyle=Fixed camera}
\ No newline at end of file
--- /dev/null
+definition module NoReferee
+
+/** This module implements a null referee which doesn't do anything */
+
+import Referee
+
+NoReferee :: !FootballField -> Referee
--- /dev/null
+implementation module NoReferee
+
+import StdEnvExt
+import Referee
+
+NoReferee :: !FootballField -> Referee
+NoReferee field = { name = "NoReferee"
+ , brain = { memory = initMem
+ , ai = randomlessRefereeAI (brain field)
+ }
+ , refActionPics = []
+ }
+
+:: Memory = Memory
+
+initMem :: Memory
+initMem = Memory
+
+brain :: !FootballField !(!RefereeInput,!Memory) -> (!RefereeOutput,!Memory)
+brain field ({RefereeInput | theBall=ballState,team1,team2},memory)
+ = ([], memory)
where\r
theBall = getFootball ballState (team1 ++ team2)\r
(compTeam,(studentTeam,studentHome)) = if (stringStarts (nameOf team1) base_TeamName_Opponent_Passing) (team1,(team2,East)) (team2,(team1,West))\r
- ballKickoff = {zero & px = if (studentHome == West) (scale -0.5 field.flength + scale 2.0 penalty_area_depth) (scale 0.5 field.flength - scale 2.0 penalty_area_depth)}\r
-// ballKickoff = {zero & px = if (studentHome == West) (scale -0.5 field.flength + penalty_area_depth) (scale 0.5 field.flength - penalty_area_depth)}\r
+ ballKickoff = {zero & px = if (studentHome == West) (scale -0.5 field.flength + penalty_area_depth) (scale 0.5 field.flength - penalty_area_depth)}\r
getMessagesForTooFarPlayers = map (\fb -> TellMessage ("Your player with number " <+++ fb.playerID.playerNr <+++ " moved further than 10 meters"))\r
(north_pole,south_pole) = goal_poles field\r
\r
--- /dev/null
+system module Team_Harmless
+
+import Team
+
+:: NumPlayers :== Int
+:: Difficulty = Auto | Level Int
+
+// import this module and add the team 'Harmless (n, level)' in Game\Team.icl
+MostlyHarmless :: (!NumPlayers,!Difficulty) Home FootballField -> Team
+
+Harmless :== MostlyHarmless (11,Auto)
*/\r
\r
import Footballer\r
-import FootballerFunctions\r
-import StdMaybe\r
\r
Team_Student_Passing :: !Home FootballField -> Team\r
Team_Student_Passing home field = if (home == West) team (mirror field team)\r
where\r
- team = [ {footballer {clubName=club,playerNr=nr} nr & pos = toPosition (scale (0.5*x) field.flength,scale (0.5*y) field.fwidth),nose = rad (dir*pi)}\r
+ team = [ {footballer {clubName=club,playerNr=nr} & pos = toPosition (scale (0.5*x) field.flength,scale (0.5*y) field.fwidth),nose = rad (dir*pi)}\r
\\ (x,y) <- positions\r
& nr <- [2..]\r
& dir <- noses\r
,( 0.43,-0.05)\r
]\r
noses = [1.8,0.0,1.5,0.5,1.2,0.2]\r
- footballer playerID nr = {defaultFootballer playerID & name = "Peter88_" +++ toString nr, brain = {memory=False, ai=mind (getOps field home)}}\r
- \r
+ footballer playerID = defaultFootballer playerID // implement your footballer here\r
+\r
base_TeamName_Student_Passing :: String\r
base_TeamName_Student_Passing = "Student Passing"\r
-\r
-\r
-:: Ops = {\r
- c :: Metre Metre -> Bool, \r
- a :: Metre Metre -> Metre, \r
- s :: Metre Metre -> Metre,\r
- g :: Position}\r
-\r
-getOps :: !FootballField !Home -> Ops\r
-getOps field West = {c=(<), a=(+), s=(-), g={px=scale 0.5 field.flength, py=(m 0.0)}}\r
-getOps field East = {c=(>), a=(-), s=(+), g={px= ~(scale 0.5 field.flength), py=(m 0.0)}}\r
-\r
-mind :: !Ops !(!BrainInput, !Bool) -> (!BrainOutput, !Bool)\r
-mind op (x=:{me,others}, mm)\r
-| mm || not (best (me team others) me bp) = halt (x, mm)\r
-| otherwise = (afterfix (((\f.(\(i, _).(f i, True))) o kick`) kickpos) bp (maxKickReach me)) (x, mm)\r
- where\r
- bp = (getBall x).ballPos.pxy\r
- \r
- kickpos = let f = nextPlayer others me in if (isNothing f) op.g (fromJust f).pos\r
- \r
- nextPlayer :: [Footballer] Footballer -> Maybe Footballer\r
- nextPlayer [] me = Nothing\r
- nextPlayer [x:xs] me\r
- | op.c x.pos.px me.pos.px = nextPlayer xs me\r
- | otherwise = Just x\r
- \r
- best :: [Footballer] Footballer Position -> Bool\r
- best [] _ _ = True\r
- best [x:xs] me bp\r
- | dist x.pos bp < dist me.pos bp = False\r
- | otherwise = best xs me bp\r
- \r
- afterfix after point diff (input=:{me}, m)\r
- | d < diff = after (input, m)\r
- | otherwise = (move, m)\r
- where\r
- d = dist me point\r
- a = bearing zero me point\r
- r = bearing me.nose me point\r
- v = ms (max 6.0 (toReal d))\r
- move = Move {direction=a, velocity=v} r
\ No newline at end of file
Do not change the implementation of base_TeamName_Student_Rounds.\r
*/\r
import Footballer\r
-import FootballerFunctions\r
\r
Team_Student_Rounds :: !Home !FootballField -> Team\r
Team_Student_Rounds home field = if (home == West) team (mirror field team)\r
]\r
club = base_TeamName_Student_Rounds +++ if (home==West) "_W" "_E"\r
positions = [(-0.49,0.00)]\r
- footballer playerID = {defaultFootballer playerID & name = "Peter88", brain = {memory = Void, ai = mind field}}\r
+ footballer playerID = defaultFootballer playerID // implement your footballer here\r
\r
base_TeamName_Student_Rounds :: String\r
base_TeamName_Student_Rounds = "Student Rounds"\r
-\r
-mind :: !FootballField !(!BrainInput, !Void) -> (!BrainOutput, !Void)\r
-mind field (i=:{me}, mm)\r
-| y >= (t -o) && x > (~r+o) = (fix {px= ~r,py= t} c) (i, mm)\r
-| x <= (~r+o) && y > (~t+o) = (fix {px= ~r,py= ~t} c) (i, mm)\r
-| y <= (~t+o) && x < (r -o) = (fix {px= r, py= ~t} c) (i, mm)\r
-| x >= (r -o) && y < (t -o) = (fix {px= r, py= t} c) (i, mm)\r
- where\r
- (x, y, o, c) = (me.pos.px, me.pos.py, m 4.0, m 1.5)\r
- t = scale 0.5 field.fwidth\r
- r = scale 0.5 field.flength
\ No newline at end of file
-\r
implementation module Team_Student_Slalom_Assignment\r
\r
/** Implement a solution to the slalom assignment.\r
team = [{footballer {clubName=club,playerNr=2} & pos = if (home == West) position (mirror field position)}]\r
club = base_TeamName_Student_Slalom +++ if (home == West) "_W" "_E"\r
position = {zero & px = scale -0.5 field.flength + penalty_area_depth}\r
- footballer player_id = {defaultFootballer player_id & name = "Peter88", brain = {memory = Void, ai = mind field home}}\r
+ footballer player_id = defaultFootballer player_id // implement your footballer here\r
\r
base_TeamName_Student_Slalom :: String\r
base_TeamName_Student_Slalom = "Student Slalom"\r
-\r
-mind :: !FootballField !Home !(!BrainInput, !Void) -> (!BrainOutput, !Void)\r
-mind field home (x=:{me,others}, mm)\r
-| dist (getBall x).ballPos me.pos < (maxKickReach me) = (kick {px=sig (scale 0.5 field.flength), py=m 0.0}) (x, mm)\r
-| otherwise = (fix {px=pp2 cp.px (m 2.5), py=up cp.py (m 3.5)} (maxKickReach me)) (x, mm)\r
- where\r
- (comparator, pp1, pp2, sig) = if (home == West) ((<), (-), (+), (\w.w)) ((>), (+), (-), (~))\r
- sf2 = sortBy (\x y.comparator x.pos.px y.pos.px) others\r
- sf = sf2 % (0, (length sf2) - 2)\r
- (cp, up) = closestPos (zip2 [1..] sf) (pp1 me.pos.px xWidthFootballer) (getBall x).ballPos.pxy comparator\r
- \r
-closestPos :: [(Int, Footballer)] Metre Position (Metre Metre -> Bool) -> (Position, (Metre Metre -> Metre))\r
-closestPos [] _ d _ = (d, (\x y.x))\r
-closestPos [(i, x):xs] p d c\r
-| c p x.pos.px = (x.pos, if (i rem 2 == 0) (+) (-))\r
-| otherwise = closestPos xs p d c\r
-\r
-\r
-\r
,(0.90,-0.05)\r
] \r
\r
-:: Mem = {home :: !Home, origpos :: Position}\r
+:: Mem = {home :: !Home, origpos :: Position, seed :: !RandomSeed}\r
+\r
+mirrorMem :: !Mem !FootballField -> Mem\r
+mirrorMem mm field = {mm & home=other mm.home, origpos=mirror field mm.origpos}\r
\r
Aesir :: !ClubName !Home !FootballField !Position !PlayersNumber !String -> Footballer\r
Aesir club home field position nr name\r
, effect = Nothing\r
, stamina = max_stamina\r
, health = max_health\r
- , brain = {memory = {home=home, origpos=position}, ai = mind field }\r
+ , brain = {memory=if (home == West) mm (mirrorMem mm field), ai = mind field}\r
}\r
- \r
-closerToGoal :: !Home -> Metre Metre -> Bool\r
-closerToGoal East = (<)\r
-closerToGoal West = (>)\r
-\r
-getMin :: !Home -> Metre Metre -> Metre\r
-getMin h = getMax (other h)\r
-\r
-getMax :: !Home -> Metre Metre -> Metre\r
-getMax East = (max)\r
-getMax West = (min)\r
+ where\r
+ mm = {home=West, origpos=position, seed=nullRandomSeed}\r
+ \r
+nextRandomNumber :: !Mem -> (Int, !Mem)\r
+nextRandomNumber mm=:{seed} = let (i, newseed) = random seed in (i rem 100, {mm & seed=newseed})\r
\r
mind :: !FootballField !(!BrainInput, !Mem) -> (!BrainOutput, !Mem)\r
mind field (x=:{referee,football,others,me}, mm=:{home,origpos})\r
-# mm=:{home,origpos} = if (any isEndHalf referee) {home=other home, origpos=mirror field origpos} mm\r
-| ballIsGainedBy me.playerID football\r
- | dist nextPos me.pos < (m 10.0) = kick nextPos (x, mm)\r
- | otherwise = fix nextPos (m 1.0) (x, mm)\r
-| isClosest others me ballPos.pxy = afterfix (\(_, m).(GainBall, m)) ballPos.pxy (scale 0.5 (maxKickReach me)) (x, mm)\r
-| otherwise\r
- | we_have_ball = fix {px=getMin home ballPos.pxy.px (field.flength-me.pos.px), py=me.pos.py} (m 1.0) (x, mm)\r
- | otherwise\r
- | me.stamina > 0.5 = fix origpos (m 1.0) (x, mm)\r
- | otherwise = halt (x, mm)\r
+// In case of the end of the half, mirror memory\r
+| any (isEndHalf) referee = mind field ({x & referee=filter (\x.not (isEndHalf x)) referee}, mirrorMem mm field)\r
+// In case of a pause or end, just halt\r
+| any (isPauseGame) referee || any (isGameOver) referee = halt (x, mm)\r
+// TODO:: The ball is free\r
+| ballIsFree football\r
+ // I'm closest to the ball\r
+ | isClosest us me ballPos.pxy = afterfix (\(_,_).(GainBall, mm)) ballPos.pxy prec (x, mm)\r
+ // TODO: Some other player is the closest to the ball\r
+ | otherwise = halt (x, mm)\r
+// TODO: We have the ball\r
+| any (\x.ballIsGainedBy x.playerID football) us = halt (x, mm)\r
+ // TODO: I have the ball\r
+ | ballIsGainedBy me.playerID football\r
+ // TODO: Someone else has the ball\r
+ | otherwise = halt (x, mm)\r
+// TODO: The ball is with the others\r
+| otherwise = halt (x, mm)\r
where\r
- us = me team others\r
ballPos = (getBall x).ballPos\r
- nextPos = nextPlayer home field us me.pos\r
- we_have_ball = or (map (\x.ballIsGainedBy x.playerID football) us)\r
-\r
-nextPlayer :: !Home !FootballField [Footballer] Position -> Position\r
-nextPlayer home field xs pos\r
-# xs = filter (\x.closerToGoal home x.pos.px pos.px) xs\r
-| xs == [] = centerOfGoal (other home) field\r
-| otherwise = (minListBy (\x y.(dist x.pos pos) < (dist y.pos pos)) xs).pos\r
-\r
+ them = me opponents others\r
+ us = me team others\r
+ prec = scale 0.5 (maxKickReach me)\r
+ \r
closest :: [Footballer] Position -> Footballer\r
closest xs p = minListBy (\x y.(dist x.pos p) < (dist y.pos p)) xs\r
\r
isClosest :: [Footballer] Footballer Position -> Bool\r
isClosest xs x p = closest [x:xs] p == x\r
\r
-afterfix :: (FootballerAI m) !Position !Metre !(!BrainInput, m) -> (BrainOutput, m)\r
-afterfix after point diff (input=:{me}, m)\r
-| d < diff = after (input, m)\r
-| otherwise = (move, m)\r
+closerToGoal :: !Home -> Metre Metre -> Bool\r
+closerToGoal East = (<)\r
+closerToGoal West = (>)\r
+\r
+distToLine :: Position Position Position -> Metre\r
+distToLine a b c = (abs (((b.px-a.px)*(a.py-c.py))-((a.px-c.px)*(b.py-a.py))))/d\r
+ where\r
+ (*) m1 m2 = (m (toReal m1)/(toReal m2))\r
+ (/) m1 m2 = (m (toReal m1)/(toReal m2))\r
+ d = sqrt(((b.px-a.px)*(b.px-a.px))+((b.py-a.py)*(b.py-a.py)))\r
+\r
+//afterfix :: (FootballerAI m) !Position !Metre !(!BrainInput, !Mem) -> (BrainOutput, !Mem)\r
+afterfix after point diff (input=:{me,others}, mm)\r
+# (i, mm) = nextRandomNumber mm\r
+| d < diff = after (input, mm)\r
+// There is a enemy standing in the way and I feel like it\r
+| (dist closestOpp me.pos) < (maxTackleReach me) && i < 15 = (Tackle closestOpp.playerID (ms 6.0), mm)\r
+// There is no enemy standing in the way\r
+| otherwise = (move, mm)\r
where\r
+ closestOpp = closest (me opponents others) me.pos\r
d = dist me point\r
a = bearing zero me point\r
r = bearing me.nose me point\r