490 likes | 621 Views
A UML2 Profile for Multi-Agent Systems. Jacques Robin. meta-class. *. ProfileApplication. Package. Class. *. *. *. *. Profile. Stereotype. ExtensionEnd. Extension. icon. Image. Property. Association. UML2 Profiles. Self-extension mechanism to customize UML2 towards:
E N D
A UML2 Profile forMulti-Agent Systems Jacques Robin
meta-class * ProfileApplication Package Class * * * * Profile Stereotype ExtensionEnd Extension icon Image Property Association UML2 Profiles • Self-extension mechanism to customize UML2 towards: • Specific application families (i.e., multi-agent simulations) • Specific implementation platforms (i.e., EJB, .net, web services) • A profile is a set of stereotypes • Concrete syntax: <<string>> and/or icon • Stereotypes are specializations of meta-classes from the UML2 meta-model UML2 Superstructure Meta-Model UML2 Extension/Customization Language Meta-Model
MAS ReflexAgent 2..* ReasoningComponent Agent MAS Environment Agent ReasoningComponent 1..* 1..* Sensor 1..* 1..* 1..* 1..* ReflexAgent ReflexComponent Actuator Percept Sensor Actuator AgentAction 1..* AutomataAgent GoalBasedAgent AutomataAgent Agent GoalBasedAgent AutomataAgent 1..* 3..* 4..* Goal ReasoningComponent ReasoningComponent Sensor EnvironmentStateModel PerceptInterpretationComponent EnvironmentStateModel GoalInitializationComponent ModelInitializationComponent GoalUpdateComponent RamificationComponent GoalBasedBehaviorStrategyComponent ModelBasedBehaviorStrategyComponent ModelBasedBehaviorStrategyComponent Actuator MOF Meta-Model of a Simple Multi-Agent Simulations Modeling Language (MASML)
ReflexKBAgent ReflexAgent ReflexComponent ReflexKBAgent ReflexKBComponent ReflexKB PersistentKB KBAgent KBComponent context ReflexKBComponent inv VolatileKB.isEmpty() GoalBasedKBAgent GoalBasedAgent AutomataKBAgent AutomataAgent 6..* GoalBasedKBAgent KBComponent 3 ..* 4 ..* 4..* AutomataKBAgent KBComponent 4 ..* KBAgent GoalKB EnvironmentStateModelKB KBAgent EnvironmentStateModelKB EnvironmentStateModel VolatileKB Goal VolatileKB EnvironmentStateModel MOF Meta-Model of a Simple Multi-Agent Simulations Modeling Language (MASML) KBAgent 1..* Agent ReasoningComponent KnowledgeBase KBSentence 1..* 1..* PersistentKB KBAgent KBComponent VolatileKB 0..*
MASML Meta-Model UML2 Meta-Model Component isActive = true Component Signal Package Model PackagableElement * TypedElement UML2 Profile for MAS MAS Environment Agent ReasoningComponent Sensor Port Actuator Percept AgentAction EnvironmentStateModel KnowledgeBase KBSentence
Formula Functor: Connective <<enumeration>> Quantifier <<enumeration>> Connective arg arg arg arg 1..2 1..2 1..* * Symbol name: String QuantifierExpression Functor: Quantifier Full Classical First-Order Logic Metamodel PredicateSymbol functor arg * * VariableSymbol NonFunctionalTerm ConstantSymbol Atom Term * functor FunctionalTerm FunctionSymbol
Formula Functor: Connective <<enumeration>> Quantifier <<enumeration>> Connective arg arg arg arg 1..2 1..2 1..* * Symbol name: String QuantifierExpression Functor: Quantifier Fluent Calculus Metamodel PredicateSymbol functor arg * * VariableSymbol NonFunctionalTerm ConstantSymbol Term Atom * functor FunctionalTerm FunctionSymbol Situation * Action Agent * 1..* * 1..* 1..* Property Object PureStateFormula HoldsAtom State * Fluent Eternal PrecondFormula PrecondAtom
UML2 Profile for the Fluent Calculus State Fluent Property Constraint
Case-Studies for Agent andGame Related Extensions • Small scale: soccer penalty simulation • Large scale: Rollerslam simulation gameOver, goal action(s,legs,shoot(2)) action(k,legs,move(right)) actions(k,legs,move(right)) action(k,hands,grab(yes)) action(s,legs,shoot(3)) action(k,legs,move(right)) gameOver, nogoal
<<enumeration>> SimStateKind created running stopped <<Interface>> IPenaltySim <<create>> +new() +run() +stop() +getSimState(): SimStateKind PenaltySim Specification Class Diagram <<Subject Component>> <<MAS>> PenaltySim
<<Subject Component>> <<MAS>> PenaltySim -state:SimStateKind <<create>> -new(): -getSimState(): SimStateKind -run() -stop() - connect(RIPort:Port,PIPort:Port) PenaltySim SpecificationComponent Diagram IPenaltySim PPenaltySim
PenaltySim SpecificationProtocol State Machine new() created run() run() running stopped stop()
<<enumeration>> Coord 0 1 2 <<enumeration>> Dir left right <<enumeration>> Role shooter keeper <<enumeration>> ChannelStateKind created perceptsReceived perceptsSensed actionReceived actionSensed <<enumeration>> AgStateKind empty initialized perceptsReceived actionChosen actionSent <<AgentAction>> MoveAction to: Dir <<AgentAction>> KickAction to: XCoord <<enumeration>> EnvStateKind empty initialized actionsReceived perceptsUpdated perceptsSent <<Percept>> XYCoord x:Coord y:Coord PenaltySim Realization Class Diagram 1 <<Subject Component>> <<MAS>> PenaltySim 2 <<ReflexAgent>> Player <<AgentAction>> PlayerAction Ball * 9 Goal 3 <<AgentAction>> LegAction <<AgentAction>> ArmAction FieldZone FieldObject <<AgentAction>> GrabAction
<<Interface>> IPlayer <<create>> +new() +initialize() +getState(): AgStateKind +getRole(): Role +reason(percepts:XYCoord[3]{ordered}): PlayerAction <<Sensor>> Eyes <<signal>> +sense():XYCoord[3]{ordered} <<Effector>> Arms +do(role:Role,action:ArmAction) PenaltySim Realization Class Diagram 2 <<Subject Component>> <<MAS>> PenaltySim 2 2 <<Effector>> Legs +do(role:Role,action:LegAction) 2 <<use>> <<use>> <<ReflexAgent>> Player <<use>> <<ReflexAgent>> Kicker <<ReflexAgent>> Keeper
<<Sensor>> GetActions <<signal>> +getAction(role:Role):PlayerAction <<Effector>> SendPercepts +sendPercept(role:Role):XYCoord[3]{ordered} PenaltySim Realization Class Diagram 3 <<Subject Component>> <<MAS>> PenaltySim <<Environment>> <<AutomataAgent>> PenaltyEnv <<use>> <<use>> <<Interface>> IPenaltyEnv <<create>> +new() +initialize() +getEnvState(): EnvStateKind -reason(shooterAction:PlayerAction{in}, keeperAction:PlayerAction{in}, shooterPercepts:XYCoord[3]{out,ordered}, keeperPercepts:XYCoord[3]{out, ordered})
<<Interface>> ICommunicationChanel <<create>> +new() +getChannelState:ChannelStateKind <<Sensor>> GetActions <<signal>> +getAction(role:Role):PlayerAction <<Effector>> SendPercepts +sendPercept(role:Role):XYCoord[3]{ordered} <<Effector>> Legs +do(role:Role,action:LegAction) <<Sensor>> Eyes <<signal>> +sense():XYCoord[3]{ordered} <<Effector>> Arms +do(role:Role,action:ArmAction) PenaltySim Realization Class Diagram 4 <<Subject Component>> <<MAS>> PenaltySim <<Component>> CommincationChannel
<<Component>> CommunicationChannel <<create>> -new() -getChannelState:ChannelStateKind <<signal>> -sendPercepts(role:Role):XYCoord[3]{ordered} -sense():XYCoord[3]{ordered} <<signal>> -do(role:Role,action:LegAction) <<signal>> -do(role:Role,action:ArmAction) -getAction(role:Role):Action PenaltySim Realization Component Diagram <<Subject Component>> <<MAS>> PenaltySim REnv RPlayer[2] RChannel PEnv PPlayer <<Environment>> <<AutomataAgent>> PenaltyEnv <<create>> -new() -initialize() -getEnvState(): EnvStateKind -reason(shooterAction:PlayerAction{in}, keeperAction:PlayerAction{in}, shooterPercepts:XYCoord[3]{out,ordered}, keeperPercepts:XYCoord[3]{out, ordered}) <<ReflexAgent>> Player <<create>> -new() -initialize() -getState(): AgStateKind -getRole(): String -reason(percepts:XYCoord[3]{ordered}): PlayerAction RLegs REyes RGetActions RSendPercepts PChannel PEyes[2] PLegs[2] <<Agent>> Shooter PGetActions RArms PArms <<Agent>> Keeper PSendPercepts
PenaltySim RealizationComposite Structure Diagram rs:RPlayer re:REnv <<MAS>> : PenaltySim pe;PEnv <<Environment>> <<AutomataAgent>> : PenaltyEnv <<SubjectComponent>> <<MAS>> penaltySim <<create>> -new() rga:RGetActions rsp:RSendPercepts pga:RGetActions psp:RSendPercepts <<Component>> : CommunicationChannel rls:RLegs pls:PLegs <<Reflex Agent>> : Shooter res:REyes pes:PEyes ps:PPlayer plk:RLegs rak:RArms pc:PChannel rlk:RLegs pak:PArms <<ReflexAgent>> : Keeper rek:REyes pk:PPlayer pek:PEyes rc:RChannel rk:RPlayer
PenaltySim RealizationActivity Diagram 1 PenaltySim.new() PPenaltySim.new() connect(rs,ps) connect(rk,pk) connect(re,pe) PenaltyEnv.new() connect(rc,pc) connect(rls,pls) Shooter.new() connect(res,pes) connect(rlk,plk) connect(rak,pak) Keeper.new() connect(rek,pek) connect(rga,pga) CommunicationChannel.new() connect(rsp,psp)
k.sense() s.sense() stop() e.getAction(k) e.getAction(s) PenaltySim Realization Activity Diagram 2 run() s:Shooter e:PenaltyEnv k:Keeper e.initialize() k.initialize() s.initialize() sp:XYCoord[3] e.sendPercepts(s) e.sendPercepts(k) kp:XYCoord[3] k.reason(kp) s.reason(sp) sa:LegAction ka:PlayerAction s.do(shooter,sa) k.do(keeper,ka) sa:LegAction ka:PlayerAction e.reason(sa,ka,sp,kp)
<<Effector>> Legs +do(Role,LegAction) <<enumeration>> Coord 0 1 2 <<enumeration>> Role shooter keeper <<enumeration>> Dir left right <<Interface>> IPlayer <<create>> +new() +initialize() +getState(): AgStateKind +getRole(): Role <<AgentAction>> KickAction to: Coord <<AgentAction>> MoveAction to: Dir <<Percept>> XYCoord x:Coord y:Coord <<Sensor>> Eyes <<signal>> +sense():XYCoord[3]{ordered} <<Effector>> Arms +do(Role,ArmAction) Player Specification Class Diagram FieldObj Ball <<AgentAction>> PlayerAction <<Subject Component>> <<ReflexAgent>> Player <<use>> * <<use>> <<AgentAction>> LegAction <<AgentAction>> ArmAction <<use>> <<ReflexAgent>> Kicker <<ReflexAgent>> Keeper <<AgentAction>> GrabAction <<enumeration>> AgStateKind empty initialized perceptsReceived actionChosen actionSent
<<ReflexAgent>> Shooter -reason(percepts:XYCoord[3]{ordered}): PlayerAction <<ReflexAgent>> Shooter -reason(percepts:XYCoord[3]{ordered}): LegAction Player Specification Component Diagram IPlayer PPlayer <<Subject Component>> <<ReflexAgent>> Player <<create>> -new() -getState(): AgStateKind -getRole(): String -reason(percepts:XYCoord[3]{ordered}): PlayerAction <<sensor>> Eyes REyes <<effector>> Legs RLegs <<effector>> Arms RArms
Player SpecificationProtocol State Machine new() empty initialize() initialized CommunicationChannler^self.sense() perceptsReceived CommunicationChannler^self.sense() reason(percepts:XYCoord[3]{ordered}) actionSent actionChosen self^CommunicationChannel.do(Role,Action)
<<Effector>> Legs +do(Role = shooter,LegAction) <<Interface>> IPlayerReflexReasoner <<create>> +new() +reason(percepts:XYCoord[3]{ordered}):LegAction <<Sensor>> Eyes <<signal>> +sense():XYCoord[3]{ordered} Shooter Realization Class Diagram <<Subject Component>> <<ReflexAgent>> Shooter <<ReflexReasoner>> ShooterReflexReasoner
Shooter Realization Component Diagram IPlayer PPlayer <<Subject Component>> <<ReflexAgent>> Shooter REyes <<sensor>> Eyes <<effector>> Legs RLegs RPRR IPlayerReflexReasoner PPRR REyes <<ReflexReasoner>> ShooterReflexReasoner <<create>> -new() +reason(percepts:XYCoord[3]{ordered}):LegAction <<sensor>> Eyes <<effector>> Legs RLegs
<<Subject Component>> <<ReflexAgent>> Shooter <<create>> -new() Shooter RealizationComposite Structure Diagram ps:PPlayer <<ReflexAgent>> : Shooter rlsrr:RLegs rls:RLegs resrr:REyes <<ReflexReasonert>> : ShooterReflexReasoner <<delegate>> <<delegate>> res:REyes psrr:PPRR rsrr:RPRR
Shooter Realization Activity Diagram 1 Shooter.new() connect(rsrr,psrr) delegate(resrr,res) ShooterReflexReasoner..new() delegate(rlsrr,rls)
mr = MoveAction.new() ml = MoveAction.new() mr.to = right ml.to = left sa = mr sa = ml klr = KickAction.new() kr = KickAction.new() kl = KickAction.new() klr.to = 0 or klr = 1 or klr = 2 kr.to = 0 kl.to = 2 sa = klr sa = kr sa = kl Shooter Realization Activity Diagram 2 sp:XYCoord[3]{ordered} Shooter.reason(sp) ballX = sp[3].x ballY = sp[3].y shooterX = sp[1].x keeperX = sp[2].x [shooterX < ballX] [ballX < shooterX] [shooterX = ballX] [ballY = 0] [else] [shooterX < keeperX] [keeperX < shooterX] [shooter X = keeper X] sa = void sa:LegAction
<<Effector>> Legs +do(Role = keeper,LegAction) <<Effector>> Arms +do(Role = keeper,ArmAction) <<Interface>> IPlayerReflexReasoner <<create>> +new() +reason(percepts:XYCoord[3]{ordered}):PlayerAction <<Sensor>> Eyes <<signal>> +sense():XYCoord[3]{ordered} Keeper Realization Class Diagram <<Subject Component>> <<ReflexAgent>> Keeper <<ReflexReasoner>> KepperReflexReasoner
<<ReflexReasoner>> KeeperReflexReasoner <<create>> -new() +reason(percepts:XYCoord[3]{ordered}):PlayerAction Keeper Realization Component Diagram IPlayer PPlayer RLegs <<Subject Component>> <<ReflexAgent>> Keeper REyes <<effector>> Legs <<sensor>> Eyes <<effector>> Arms RArms RPRR IPlayerReflexReasoner PPRR REyes RLegs <<sensor>> Eyes <<effector>> Legs <<effector>> Arms RArms
<<Subject Component>> <<ReflexAgent>> Keeper <<create>> -new() Keeper RealizationComposite Structure Diagram pk:PPlayer <<ReflexAgent>> : Keeper rlk:RLegs <<delegate>> rlkrr:RLegs rekrr:REyes <<ReflexReasoner>> : KeeperrReflexReasoner <<delegate>> rek:REyes rakrr:RArms <<delegate>> rak:RArms pkrr:PPRR rkrr:RPRR
Shooter Realization Activity Diagram 1 Shooter.new() connect(rkrr,pkrr) delegate(rlkrr,rlk) ShooterReflexRuleBaseComponent..new() delegate(rakrr,rak) delegate(rekrr,rek)
mr = MoveAction.new() ml = MoveAction.new() mr.to = right ml.to = left sa = mr sa = ml Keeper Realization Activity Diagram 2 kp:XYCoord[3]{ordered} Keeperr.reason(sp) ballX = sp[3].x ballY = sp[3].y shooterX = sp[1].x keeperX = sp[2].x [keeperX < ballX] [ballX < keeperX] [keeperX = ballX] [ballY = keeperY] [else] sa = GrabAction.new() sa = void ka:PlayerAction
<<Subject Component>> <<Environment>> <<AutomataAgent>> PenaltyEnv <<enumeration>> Coord 0 1 2 <<enumeration>> Dir left right <<enumeration>> Role shooter keeper <<AgentAction>> MoveAction to: Dir <<AgentAction>> KickAction to: Coord <<Percept>> XYCoord x:Coord y:Coord <<Effector>> SendPercepts +sendPercept(role:Role):XYCoord[3]{ordered} <<Sensor>> GetActions <<signal>> +getAction(role:Role):PlayerAction PenaltyEnv Specification Class Diagram <<use>> <<use>> <<AgentAction>> PlayerAction <<Interface>> IPenaltyEnv <<create>> +new() +initialize() +getEnvState(): EnvStateKind -reason(shooterAction:PlayerAction{in}, keeperAction:PlayerAction{in}, shooterPercepts:XYCoord[3]{out,ordered}, keeperPercepts:XYCoord[3]{out, ordered}) <<AgentAction>> LegAction <<AgentAction>> ArmAction <<AgentAction>> GrabAction <<enumeration>> EnvStateKind empty initialized actionsReceived perceptsUpdated perceptsSent
PenaltyEnv SpecificationComponent Diagram IPenaltyEnv PPenaltyEnv <<Subject Component>> <<Environment>> <<AutomataAgent>> PenaltyEnv <<create>> -new() initialize() -getEnvState(): EnvStateKind -reason(shooterAction:PlayerAction{in}, keeperAction:PlayerAction{in}, shooterPercepts:XYCoord[3]{out,ordered}, keeperPercepts:XYCoord[3]{out, ordered}) <<sensor>> GetActions RGetActions RSendPercepts <<effector>> SendPercepts
PenaltyEnv SpecificationProtocol State Machine initialize() new() empty initialized < CommunicationChannler^self.getAction(keeper,PlayerAction) CommunicationChannler^self.getAction(shooter,LegAction) actionsReceived reason(shooterAction:PlayerAction{in}, keeperAction:PlayerAction{in}, shooterPercepts:XYCoord[3]{out,ordered}, keeperPercepts:XYCoord[3]{out, ordered}) perceptsUpdated self^CommunicationChannel.sendPercepts(shooter) self^CommunicationChannel.sendPercepts(keeper) perceptsSent
<<Subject Component>> <<Environment>> <<AutomataAgent>> PenaltyEnv <<Percept>> XYCoord x:Coord y:Coord <<Sensor>> GetActions <<signal>> +getAction(role:Role):PlayerAction <<Effector>> SendPercepts +sendPercept(role:Role):XYCoord[3]{ordered} PenaltyEnv Realization Class Diagram PenaltyEnvModel <<ReflexAgent>> Shooter Ball <<ReflexAgent>> Keeper
PenaltyEnv RealizationComponent Diagram IPlayer PPlayer <<Subject Component>> <<ReflexAgent>> Shooter REyes <<sensor>> Eyes <<effector>> Legs RLegs RPRR IPlayerReflexReasoner PPRR REyes <<ReflexReasoner>> ShooterReflexReasoner <<create>> -new() +reason(percepts:XYCoord[3]{ordered}):LegAction <<sensor>> Eyes <<effector>> Legs RLegs
<<Subject Component>> <<ReflexAgent>> Shooter <<create>> -new() PenaltyEnv RealizationComposite Structure Diagram ps:PPlayer <<ReflexAgent>> : Shooter rlsrr:RLegs rls:RLegs resrr:REyes <<ReflexReasonert>> : ShooterReflexReasoner <<delegate>> <<delegate>> res:REyes psrr:PPRR rsrr:RPRR
PenaltyEnv RealizationActivity Diagram 1 Shooter.new() connect(rsrr,psrr) delegate(resrr,res) ShooterReflexReasoner..new() delegate(rlsrr,rls)
mr = MoveAction.new() ml = MoveAction.new() mr.to = right ml.to = left sa = mr sa = ml klr = KickAction.new() kr = KickAction.new() kl = KickAction.new() klr.to = 0 or klr = 1 or klr = 2 kr.to = 0 kl.to = 2 sa = klr sa = kr sa = kl PenaltyEnv Realization Activity Diagram 2 sp:XYCoord[3]{ordered} Shooter.reason(sp) ballX = sp[3].x ballY = sp[3].y shooterX = sp[1].x keeperX = sp[2].x [shooterX < ballX] [ballX < shooterX] [shooterX = ballX] [ballY = 0] [else] [shooterX < keeperX] [keeperX < shooterX] [shooter X = keeper X] sa = void sa:LegAction
<<Effector>> Legs +do(Role,LegAction) <<enumeration>> Coord 0 1 2 <<enumeration>> Role shooter keeper <<enumeration>> Dir left right <<interface>> IPlayer <<create>> +new() +initialize() +getState(): AgStateKind +getRole(): Role <<AgentAction>> KickAction to: Coord <<AgentAction>> MoveAction to: Dir <<Percept>> XYCoord x:Coord y:Coord <<Sensor>> Eyes <<signal>> +sense():XYCoord[3]{ordered} <<Effector>> Arms +do(Role,ArmAction) CommunicationChannel Specification Class Diagram FieldObj Ball <<AgentAction>> PlayerAction <<Subject Component>> <<ReflexAgent>> Player <<use>> * <<use>> <<AgentAction>> LegAction <<AgentAction>> ArmAction <<use>> <<Agent>> Kicker <<Agent>> Keeper <<AgentAction>> GrabAction <<enumeration>> AgStateKind empty initialized perceptsReceived actionChosen actionSent
<<ReflexAgent>> Shooter -reason(percepts:XYCoord[3]{ordered}): PlayerAction <<ReflexAgent>> Shooter -reason(percepts:XYCoord[3]{ordered}): LegAction CommunicationChannel SpecificationComponent Diagram IPlayer PPlayer <<Subject Component>> <<ReflexAgent>> Player <<create>> -new() -getState(): AgStateKind -getRole(): String -reason(percepts:XYCoord[3]{ordered}): PlayerAction <<sensor>> Eyes REyes <<effector>> Legs RLegs <<effector>> Arms RArms
CommunicationChannel SpecificationProtocol State Machine new() empty initialize() initialized CommunicationChannler^self.sense() perceptsReceived CommunicationChannler^self.sense() reason(percepts:XYCoord[3]{ordered}) actionSent actionChosen self^CommunicationChannel.do(Role,Action)
<<Effector>> Legs +do(Role = shooter,LegAction) <<Interface>> IPlayerReflexReasoner <<create>> +new() +reason(percepts:XYCoord[3]{ordered}):LegAction <<Sensor>> Eyes <<signal>> +sense():XYCoord[3]{ordered} CommunicationChannel RealizationClass Diagram <<Subject Component>> <<ReflexAgent>> Shooter <<ReflexReasoner>> ShooterReflexReasoner
CommunicationChannel RealizationComponent Diagram IPlayer PPlayer <<Subject Component>> <<ReflexAgent>> Shooter REyes <<sensor>> Eyes <<effector>> Legs RLegs RPRR IPlayerReflexReasoner PPRR REyes <<ReflexReasoner>> ShooterReflexReasoner <<create>> -new() +reason(percepts:XYCoord[3]{ordered}):LegAction <<sensor>> Eyes <<effector>> Legs RLegs
<<Subject Component>> <<ReflexAgent>> Shooter <<create>> -new() CommunicationChannel RealizationComposite Structure Diagram ps:PPlayer <<ReflexAgent>> : Shooter rlsrr:RLegs rls:RLegs resrr:REyes <<ReflexReasonert>> : ShooterReflexReasoner <<delegate>> <<delegate>> res:REyes psrr:PPRR rsrr:RPRR
CommunicationChannel RealizationActivity Diagram 1 Shooter.new() connect(rsrr,psrr) delegate(resrr,res) ShooterReflexReasoner..new() delegate(rlsrr,rls)
mr = MoveAction.new() ml = MoveAction.new() mr.to = right ml.to = left sa = mr sa = ml klr = KickAction.new() kr = KickAction.new() kl = KickAction.new() klr.to = 0 or klr = 1 or klr = 2 kr.to = 0 kl.to = 2 sa = klr sa = kr sa = kl CommunicationChannel RealizationActivity Diagram 2 sp:XYCoord[3]{ordered} Shooter.reason(sp) ballX = sp[3].x ballY = sp[3].y shooterX = sp[1].x keeperX = sp[2].x [shooterX < ballX] [ballX < shooterX] [shooterX = ballX] [ballY = 0] [else] [shooterX < keeperX] [keeperX < shooterX] [shooter X = keeper X] sa = void sa:LegAction