Guest User

Untitled

a guest
Apr 25th, 2018
65
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 32.96 KB | None | 0 0
  1. functor
  2. import
  3. System
  4. Application
  5. QTk at 'x-oz://system/wp/QTk.ozf'
  6. Open at 'x-oz://system/Open.ozf'
  7. OS
  8. define
  9.  
  10. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  11. % Constants
  12.  
  13. UNIT_DELAY = 1000
  14. INIT_RESOURCES = resources(food:100 wood:100 stone:100 steel:100)
  15. GOAL = resources(food:200 wood:200 stone:200 steel:200)
  16. BAG_LIMIT = 10
  17. PLAYER_DEFAULT_STRENGTH = 1
  18. % A player _with_ a weapon, not the strength of the weapon:
  19. PLAYER_WEAPON_STRENGTH = 3
  20. TOWER_INIT_POINTS = 20
  21. TOWER_VISIBLE_DIST = 4
  22. TOWER_POWER_DIST = 2
  23. COST_WEAPON = resources(food:0 wood:0 stone:0 steel:25)
  24. COST_PLAYER = resources(food:10 wood:0 stone:0 steel:0)
  25. COST_TOWER = resources(food:0 wood:50 stone:50 steel:0)
  26.  
  27. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  28. % Utilities functions
  29.  
  30. proc {UnitDelay}
  31. {Delay UNIT_DELAY}
  32. end
  33.  
  34. % Calculate the sum of the resources
  35. fun {ResourcesSum Res}
  36. Res.food + Res.wood + Res.stone + Res.steel
  37. end
  38.  
  39. fun {GetResourceType SquareType}
  40. case SquareType
  41. of forest then wood
  42. [] field then food
  43. [] quarry then stone
  44. [] mine then steel
  45. else none
  46. end
  47. end
  48.  
  49. % Wait X and Y. As soon as one of the two variables gets bound,
  50. % return the value, which is 1 if X is bound first, or 2 if Y is bound first.
  51. fun {WaitTwo X Y}
  52. Ret
  53. in
  54. thread
  55. {Wait X}
  56. if {Not {IsDet Ret}} then Ret = 1 end
  57. end
  58. thread
  59. {Wait Y}
  60. if {Not {IsDet Ret}} then Ret = 2 end
  61. end
  62. Ret
  63. end
  64.  
  65. % Creates a port object
  66. fun {NewPortObject Behaviour InitState}
  67. proc {MsgLoop Stream State}
  68. case Stream
  69. of nil then skip
  70. [] Msg|OtherMessages then
  71. {MsgLoop OtherMessages {Behaviour Msg State}}
  72. end
  73. end
  74. Stream
  75. in
  76. thread {MsgLoop Stream InitState} end
  77. {NewPort Stream}
  78. end
  79.  
  80. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  81. % Port objects: Home, Player and Square
  82.  
  83. % Creates a home object.
  84. % HomePort: get the port to which we can send messages.
  85. % StateStream: get a stream containing all the states.
  86. % The state contains only the number of resources.
  87. % The purpose of the state stream is to be notified when the number of resources change,
  88. % so we can, for example, update the user interface.
  89. proc {CreateHome ?HomePort ?StateStream}
  90. StatePort
  91.  
  92. fun {HandleMsg Msg State}
  93. case Msg
  94. of getNbResources(?Res) then
  95. Res = State
  96. State
  97.  
  98. [] addResources(Res) then
  99. NewState = resources(food: Res.food + State.food
  100. wood: Res.wood + State.wood
  101. stone: Res.stone + State.stone
  102. steel: Res.steel + State.steel)
  103. in
  104. {Send StatePort NewState}
  105. NewState
  106.  
  107. [] removeResources(Res ?OK) then
  108. if State.food < Res.food orelse
  109. State.wood < Res.wood orelse
  110. State.stone < Res.stone orelse
  111. State.steel < Res.steel then
  112. OK = false
  113. State
  114. else
  115. NewState = resources(food: State.food - Res.food
  116. wood: State.wood - Res.wood
  117. stone: State.stone - Res.stone
  118. steel: State.steel - Res.steel)
  119. in
  120. OK = true
  121. {Send StatePort NewState}
  122. NewState
  123. end
  124. end
  125. end
  126. in
  127. {NewPort StateStream StatePort}
  128. {Send StatePort INIT_RESOURCES}
  129. HomePort = {NewPortObject HandleMsg INIT_RESOURCES}
  130. end
  131.  
  132. fun {CreatePlayer Team Squares Map StaticEnv}
  133. InitBag = resources(food:0 wood:0 stone:0 steel:0)
  134. PlayerPort
  135.  
  136. % Move to a surrounding square. Returns the new state.
  137. fun {Move NewX NewY State}
  138. CurX = State.pos.x
  139. CurY = State.pos.y
  140. in
  141. {UnitDelay}
  142.  
  143. % Same position
  144. if NewX == CurX andthen NewY == CurY then
  145. State
  146.  
  147. % Invalid position (out of the map)
  148. elseif NewX < 1 orelse NewY < 1
  149. orelse {Width Squares} < NewY orelse {Width Squares.1} < NewX then
  150. State
  151.  
  152. % Not one of the 8 surrounding squares
  153. elseif 1 < {Number.abs CurX - NewX} orelse 1 < {Number.abs CurY - NewY} then
  154. State
  155.  
  156. % OK, do the move
  157. else
  158. NewState
  159. Dead
  160. in
  161. {Send Squares.CurY.CurX playerOut(Team.num PlayerPort)}
  162. {Send Squares.NewY.NewX playerIn(Team.num PlayerPort State.weapon ?Dead)}
  163.  
  164. if Dead then
  165. NewState = {AdjoinAt State dead true}
  166.  
  167. % If on home, deposit bag resources
  168. elseif NewX == Team.homePos.x andthen NewY == Team.homePos.y
  169. andthen 0 < {ResourcesSum State.bag} then
  170. {Send Team.home addResources(State.bag)}
  171. NewState = {AdjoinAt State bag InitBag}
  172. else
  173. NewState = State
  174. end
  175.  
  176. {AdjoinAt NewState pos pos(x:NewX y:NewY)}
  177. end
  178. end
  179.  
  180. % Exploit a resource
  181. fun {Exploit State}
  182. CurX = State.pos.x
  183. CurY = State.pos.y
  184. SquareType = Map.CurY.CurX
  185. in
  186. % Not on a resource, a unit time is lost...
  187. % TODO: handle resource stealing on an opponent's home
  188. if SquareType == normal orelse SquareType == home then
  189. {UnitDelay}
  190. State
  191.  
  192. % The bag is full
  193. elseif BAG_LIMIT =< {ResourcesSum State.bag} then
  194. {UnitDelay}
  195. State
  196.  
  197. % OK, exploit the resource
  198. else
  199. Dead
  200. Finished
  201. in
  202. % Manage fights
  203. {Send Squares.CurY.CurX beginExploit(Team.num PlayerPort State.weapon ?Dead)}
  204.  
  205. thread
  206. {UnitDelay}
  207. Finished = unit
  208. end
  209.  
  210. % There is a fight
  211. if {WaitTwo Dead Finished} == 1 then
  212. % We are dead
  213. if Dead then
  214. {Send Squares.CurY.CurX playerOut(Team.num PlayerPort)}
  215. {AdjoinAt State dead true}
  216.  
  217. % We survived, but we lost the resource
  218. else
  219. State
  220. end
  221.  
  222. % No fight, finished to exploit the resource
  223. else
  224. ResType = {GetResourceType SquareType}
  225. NewBag = {AdjoinAt State.bag ResType State.bag.ResType+1}
  226. in
  227. {Send Squares.CurY.CurX endExploit(Team.num PlayerPort)}
  228. {AdjoinAt State bag NewBag}
  229. end
  230. end
  231. end
  232.  
  233. fun {KillMe State}
  234. CurX = State.pos.x
  235. CurY = State.pos.y
  236. in
  237. % Send a message to the square to say that we are dead.
  238. % Only squares can send the kill message, BUT if the player was moving to another
  239. % square during the kill, this is not the _same_ square.
  240. {Send Squares.CurY.CurX playerOut(Team.num PlayerPort)}
  241.  
  242. {AdjoinAt State dead true}
  243. end
  244.  
  245. fun {BuildWeapon State}
  246. {UnitDelay}
  247.  
  248. % Check that the player has not already a weapon
  249. if {Not {IsDet State.weapon}} then
  250. OK
  251. in
  252. {Send Team.home removeResources(COST_WEAPON ?OK)}
  253. if OK then
  254. State.weapon = unit
  255. end
  256. end
  257.  
  258. State
  259. end
  260.  
  261. fun {BuildPlayer InitPlayerState State}
  262. OK
  263. in
  264. {UnitDelay}
  265. {Send Team.home removeResources(COST_PLAYER ?OK)}
  266. if OK then
  267. NewPlayer = {CreatePlayer Team Squares Map StaticEnv}
  268. NewBrain = {CreateBrain StaticEnv InitPlayerState}
  269. in
  270. {Embody NewPlayer NewBrain InitPlayerState}
  271. end
  272.  
  273. State
  274. end
  275.  
  276. fun {GetBrainEnv State}
  277. HomeResources
  278. VisibleTowers
  279. CurX = State.pos.x
  280. CurY = State.pos.y
  281. in
  282. {Send Team.home getNbResources(?HomeResources)}
  283. {Send Squares.CurY.CurX getVisibleTowers(?VisibleTowers)}
  284. {Wait HomeResources}
  285. {Wait VisibleTowers}
  286.  
  287. env(location: State.pos
  288. bag: State.bag
  289. resources: HomeResources
  290. weapon: {IsDet State.weapon}
  291. opponentHome: none
  292. towers: VisibleTowers)
  293. end
  294.  
  295. fun {HandleMsg Msg State}
  296. case Msg
  297. % Message from the brain
  298. of Action#BrainEnv#Dead then
  299. % If the player is already dead, do nothing
  300. if State.dead then
  301. Dead = true
  302. BrainEnv = {GetBrainEnv State}
  303. State
  304. else
  305. NewState
  306. in
  307. case Action
  308. of noop then
  309. NewState = State
  310. [] move(pos(x:NewX y:NewY)) then
  311. NewState = {Move NewX NewY State}
  312. [] exploit then
  313. NewState = {Exploit State}
  314. [] build(weapon) then
  315. NewState = {BuildWeapon State}
  316. [] build(player(InitPlayerState)) then
  317. NewState = {BuildPlayer InitPlayerState State}
  318. end
  319.  
  320. Dead = NewState.dead
  321. BrainEnv = {GetBrainEnv NewState}
  322. NewState
  323. end
  324.  
  325. [] kill then
  326. {KillMe State}
  327. end
  328. end
  329.  
  330. HomeSquare = Squares.(Team.homePos.y).(Team.homePos.x)
  331.  
  332. % Initially, the player is at its home and his bag is empty
  333. InitState = state(pos: Team.homePos
  334. bag: InitBag
  335.  
  336. % To know if the player has a weapon, do {IsDet State.weapon}.
  337. % We pass this (initially unbound) variable to the Square object,
  338. % so when the square wants to know the strength of a player,
  339. % the square doesn't need to ask the player by sending a message,
  340. % which can be very slow with the current implementation.
  341. weapon: _
  342. dead: false)
  343. State
  344. Dead
  345. in
  346. {Send HomeSquare playerIn(Team.num PlayerPort InitState.weapon ?Dead)}
  347.  
  348. % Yes, a player can be dead before he gets born, if there is an opponent's tower
  349. % near or on the player's home.
  350. if Dead then
  351. State = {AdjoinAt InitState dead true}
  352. else
  353. State = InitState
  354. end
  355.  
  356. PlayerPort = {NewPortObject HandleMsg State}
  357. PlayerPort
  358. end
  359.  
  360. % Home is the atom none or a record of the form: home(owner:TeamNum port:Port)
  361. proc {CreateSquare Pos NbTeams Home AllSquares ?SquarePort ?StateStream}
  362. StatePort
  363.  
  364. fun {GetPlayerStrength Weapon}
  365. if {IsDet Weapon} then
  366. PLAYER_WEAPON_STRENGTH
  367. else
  368. PLAYER_DEFAULT_STRENGTH
  369. end
  370. end
  371.  
  372. fun {GetFirstOpponentTower TeamNum State}
  373. fun {Step List}
  374. case List
  375. of nil then
  376. none
  377. [] Tower|OtherTowers then
  378. if Tower.owner \= TeamNum then
  379. Tower
  380. else
  381. {Step OtherTowers}
  382. end
  383. end
  384. end
  385. in
  386. % Search only in the near towers.
  387. % If the current square has an opponent tower, it's impossible that the player
  388. % is still alive, so we omit the current square here.
  389. {Step State.nearTowers}
  390. end
  391.  
  392. % A new player is on the square
  393. fun {PlayerIn TeamNum Player Weapon State ?Dead}
  394. fun {AddPlayer}
  395. NewPlayer = player(port:Player weapon:Weapon)
  396. NewPlayers = {AdjoinAt State.players TeamNum NewPlayer|State.players.TeamNum}
  397. in
  398. {AdjoinAt State players NewPlayers}
  399. end
  400.  
  401. OpponentTower = {GetFirstOpponentTower TeamNum State}
  402. in
  403. if OpponentTower == none then
  404. Dead = false
  405. {AddPlayer}
  406. else
  407. PlayerStrength = {GetPlayerStrength Weapon}
  408. in
  409. {Send OpponentTower.square weakenTower(PlayerStrength ?Dead)}
  410. if Dead then
  411. State
  412. else
  413. {AddPlayer}
  414. end
  415. end
  416. end
  417.  
  418. fun {RemovePlayer Player List}
  419. case List
  420. of nil then
  421. nil
  422. [] P|OtherPlayers andthen P.port == Player then
  423. OtherPlayers
  424. [] P|OtherPlayers then
  425. P|{RemovePlayer Player OtherPlayers}
  426. end
  427. end
  428.  
  429. % A player has quit the square
  430. fun {PlayerOut TeamNum Player State}
  431. List = {RemovePlayer Player State.players.TeamNum}
  432. NewPlayers = {AdjoinAt State.players TeamNum List}
  433. in
  434. {AdjoinAt State players NewPlayers}
  435. end
  436.  
  437. % A new tower has just been built in this square. Kill opponent's players until
  438. % there is no more opponent's players or when the tower has lost all its points.
  439. % The remaining points can be negative.
  440. proc {NewTowerKillPlayers TowerTeam State ?RemainingPoints ?NewState}
  441. proc {Step TeamNum PlayersState TowerPoints}
  442. if TeamNum == TowerTeam then
  443. {Step TeamNum+1 PlayersState TowerPoints}
  444.  
  445. elseif NbTeams < TeamNum orelse TowerPoints =< 0 then
  446. RemainingPoints = TowerPoints
  447. NewState = {AdjoinAt State players PlayersState}
  448.  
  449. else
  450. case PlayersState.TeamNum
  451. of nil then
  452. {Step TeamNum+1 PlayersState TowerPoints}
  453.  
  454. [] Player|OtherPlayers then
  455. PlayerStrength = {GetPlayerStrength Player.weapon}
  456. NewPlayersState = {AdjoinAt PlayersState TeamNum OtherPlayers}
  457. in
  458. {Send Player.port kill}
  459. {Step TeamNum NewPlayersState TowerPoints-PlayerStrength}
  460. end
  461. end
  462. end
  463. in
  464. {Step 1 State.players TOWER_INIT_POINTS}
  465. end
  466.  
  467. % A tower on another square has just been built. Kill opponent's players until
  468. % there is no more opponent's players or when the tower has lost all its points.
  469. % Returns the new state.
  470. proc {TowerKillPlayers TowerTeam TowerSquare State ?TowerIsDestroyed ?NewState}
  471. proc {Step TeamNum PlayersState}
  472. if TeamNum == TowerTeam then
  473. {Step TeamNum+1 PlayersState}
  474.  
  475. elseif NbTeams < TeamNum then
  476. TowerIsDestroyed = false
  477. NewState = {AdjoinAt State players PlayersState}
  478.  
  479. else
  480. case PlayersState.TeamNum
  481. of nil then
  482. {Step TeamNum+1 PlayersState}
  483.  
  484. [] Player|OtherPlayers then
  485. PlayerStrength = {GetPlayerStrength Player.weapon}
  486. OK
  487. in
  488. {Send TowerSquare weakenTower(PlayerStrength ?OK)}
  489. if OK then
  490. NewPlayersState = {AdjoinAt PlayersState TeamNum OtherPlayers}
  491. in
  492. {Send Player.port kill}
  493. {Step TeamNum NewPlayersState}
  494. else
  495. TowerIsDestroyed = true
  496. NewState = {AdjoinAt State players PlayersState}
  497. end
  498. end
  499. end
  500. end
  501. in
  502. {Step 1 State.players}
  503. end
  504.  
  505. % Build a tower on this square
  506. fun {BuildTower TeamNum State ?OK}
  507. if State.tower \= none then
  508. OK = false
  509. State
  510. else
  511. RemainingPoints
  512. NewState
  513. in
  514. OK = true
  515. {NewTowerKillPlayers TeamNum State ?RemainingPoints ?NewState}
  516. if RemainingPoints =< 0 then
  517. NewState
  518. else
  519. {NotifyNeighbourSquares TOWER_VISIBLE_DIST towerBuilt(TeamNum SquarePort Pos)}
  520. {AdjoinAt NewState tower tower(owner:TeamNum points:RemainingPoints)}
  521. end
  522. end
  523. end
  524.  
  525. % Weaken the tower of this square
  526. fun {WeakenTower Strength State ?OK}
  527. if State.tower == none then
  528. OK = false
  529. State
  530. else
  531. NewTowerPoints = State.tower.points - Strength
  532. NewTowerState
  533. in
  534. OK = true
  535.  
  536. if 0 < NewTowerPoints then
  537. NewTowerState = {AdjoinAt State.tower points NewTowerPoints}
  538. else
  539. NewTowerState = none
  540. {NotifyNeighbourSquares TOWER_VISIBLE_DIST towerDestroyed(SquarePort Pos)}
  541. end
  542.  
  543. {AdjoinAt State tower NewTowerState}
  544. end
  545. end
  546.  
  547. % Notify the neighbour squares with a message
  548. proc {NotifyNeighbourSquares Dist Msg}
  549. NbRows = {Width AllSquares}
  550. NbCols = {Width AllSquares.1}
  551. MinX = {Max 1 Pos.x-Dist} % Minix!
  552. MaxX = {Min NbCols Pos.x+Dist}
  553. MinY = {Max 1 Pos.y-Dist}
  554. MaxY = {Min NbRows Pos.y+Dist}
  555. StopPos = pos(x:MaxX y:MaxY)
  556.  
  557. fun {GetNextPos Pos}
  558. if Pos.x < MaxX then
  559. pos(x:Pos.x+1 y:Pos.y)
  560. else
  561. pos(x:MinX y:Pos.y+1)
  562. end
  563. end
  564.  
  565. proc {NotifyStep CurPos}
  566. if CurPos \= Pos then
  567. {Send AllSquares.(CurPos.y).(CurPos.x) Msg}
  568. end
  569.  
  570. if CurPos \= StopPos then
  571. {NotifyStep {GetNextPos CurPos}}
  572. end
  573. end
  574. in
  575. {NotifyStep pos(x:MinX y:MinY)}
  576. end
  577.  
  578. fun {CanKill TowerPos}
  579. {Number.abs Pos.x-TowerPos.x} =< TOWER_POWER_DIST
  580. andthen {Number.abs Pos.y-TowerPos.y} =< TOWER_POWER_DIST
  581. end
  582.  
  583. % A tower has been built on an neighbour square
  584. fun {AddTower TeamNum Square TowerPos State}
  585. NewTower = tower(location:TowerPos owner:TeamNum square:Square)
  586. NewState
  587. TowerIsDestroyed
  588. in
  589. if {CanKill TowerPos} then
  590. NewPlayersState
  591. in
  592. {TowerKillPlayers TeamNum Square State ?TowerIsDestroyed ?NewPlayersState}
  593. if TowerIsDestroyed then
  594. NewState = NewPlayersState
  595. else
  596. NewState = {AdjoinAt NewPlayersState nearTowers NewTower|State.nearTowers}
  597. end
  598. else
  599. TowerIsDestroyed = false
  600. NewState = State
  601. end
  602.  
  603. if TowerIsDestroyed then
  604. NewState
  605. else
  606. % If we have received the message to add a tower, then it's at least visible
  607. {AdjoinAt NewState visibleTowers NewTower|State.visibleTowers}
  608. end
  609. end
  610.  
  611. % A tower has been destroyed on an neighbour square
  612. fun {RemoveTower Square TowerPos State}
  613. fun {RemoveInList Towers}
  614. {Filter Towers fun {$ Tower} Tower.square \= Square end}
  615. end
  616.  
  617. NewNearState
  618. in
  619. if {CanKill TowerPos} then
  620. NewNearState = {AdjoinAt State nearTowers {RemoveInList State.nearTowers}}
  621. else
  622. NewNearState = State
  623. end
  624.  
  625. {AdjoinAt NewNearState visibleTowers {RemoveInList State.visibleTowers}}
  626. end
  627.  
  628. fun {GetVisibleTowers State}
  629. % The return value must be suitable for the brain environment.
  630. % If this square has a tower, add it to the list.
  631. if State.tower == none then
  632. State.visibleTowers
  633. else
  634. tower(location:Pos owner:State.tower.owner) | State.visibleTowers
  635. end
  636. end
  637.  
  638. % Kill all the opponent's players.
  639. % Returns the new PlayersState.
  640. fun {KillAllOpponentPlayers WinnerTeam PlayersState}
  641. for TeamNum in 1..NbTeams do
  642. if TeamNum \= WinnerTeam then
  643. for Player in PlayersState.TeamNum do
  644. {Send Player.port kill}
  645. end
  646. end
  647. end
  648.  
  649. {AdjoinAt InitPlayers WinnerTeam PlayersState.WinnerTeam}
  650. end
  651.  
  652. % The player begins to exploit the resource.
  653. fun {BeginExploit TeamNum Player Weapon State ?Dead}
  654. % Add the player to the exploitation list of his team.
  655. NewPlayer = player(port:Player weapon:Weapon dead:Dead)
  656. NewExpl = {AdjoinAt State.exploitations TeamNum
  657. NewPlayer|State.exploitations.TeamNum}
  658.  
  659. fun {SearchOtherTeam OtherTeamNum}
  660. if NbTeams < OtherTeamNum then
  661. none
  662. elseif OtherTeamNum \= TeamNum
  663. andthen NewExpl.OtherTeamNum \= nil then
  664. OtherTeamNum
  665. else
  666. {SearchOtherTeam OtherTeamNum+1}
  667. end
  668. end
  669.  
  670. fun {GetTotalStrength Team}
  671. fun {Step List Total}
  672. case List
  673. of nil then
  674. Total
  675. [] Player|OtherPlayers then
  676. {Step OtherPlayers Total + {GetPlayerStrength Player.weapon}}
  677. end
  678. end
  679. in
  680. {Step NewExpl.Team 0}
  681. end
  682.  
  683. proc {SetDead Team IsDead}
  684. proc {Step List}
  685. case List
  686. of nil then
  687. skip
  688. [] Player|OtherPlayers then
  689. Player.dead = IsDead
  690. {Step OtherPlayers}
  691. end
  692. end
  693. in
  694. {Step NewExpl.Team}
  695. end
  696.  
  697. OtherTeamNum = {SearchOtherTeam 1}
  698. in
  699. % Peace.
  700. if OtherTeamNum == none then
  701. {AdjoinAt State exploitations NewExpl}
  702.  
  703. % Fight!
  704. else
  705. NewTeamStrength = {GetTotalStrength TeamNum}
  706. OtherTeamStrength = {GetTotalStrength OtherTeamNum}
  707. NewTeamIsDead = NewTeamStrength =< OtherTeamStrength
  708. OtherTeamIsDead = OtherTeamStrength =< NewTeamStrength
  709. in
  710. {SetDead TeamNum NewTeamIsDead}
  711. {SetDead OtherTeamNum OtherTeamIsDead}
  712.  
  713. {AdjoinAt State exploitations InitExploitations}
  714. end
  715. end
  716.  
  717. % The player has finished to exploit the resource.
  718. % Remove the player from the exploitation list.
  719. fun {EndExploit TeamNum Player State}
  720. fun {RemoveInList Players}
  721. {Filter Players fun {$ P} P.port \= Player end}
  722. end
  723.  
  724. NewPlayers = {RemoveInList State.exploitations.TeamNum}
  725. NewExplState = {AdjoinAt State.exploitations TeamNum NewPlayers}
  726. in
  727. {AdjoinAt State exploitations NewExplState}
  728. end
  729.  
  730. fun {HandleMsg Msg State}
  731. % We send the new state to the StatePort only when the UI should be updated
  732. case Msg
  733. of playerIn(TeamNum Player Weapon ?Dead) then
  734. NewState = {PlayerIn TeamNum Player Weapon State ?Dead}
  735. in
  736. {Send StatePort NewState}
  737. NewState
  738.  
  739. [] playerOut(TeamNum Player) then
  740. NewState = {PlayerOut TeamNum Player State}
  741. in
  742. {Send StatePort NewState}
  743. NewState
  744.  
  745. [] buildTower(TeamNum ?OK) then
  746. NewState = {BuildTower TeamNum State ?OK}
  747. in
  748. if OK then
  749. {Send StatePort NewState}
  750. end
  751. NewState
  752.  
  753. [] weakenTower(Strength ?OK) then
  754. NewState = {WeakenTower Strength State ?OK}
  755. in
  756. % destroyed
  757. if OK andthen NewState.tower == none then
  758. {Send StatePort NewState}
  759. end
  760. NewState
  761.  
  762. [] towerBuilt(TeamNum Square Pos) then
  763. {AddTower TeamNum Square Pos State}
  764.  
  765. [] towerDestroyed(Square Pos) then
  766. {RemoveTower Square Pos State}
  767.  
  768. [] getVisibleTowers(?Towers) then
  769. Towers = {GetVisibleTowers State}
  770. State
  771.  
  772. [] beginExploit(TeamNum Player Weapon ?Dead) then
  773. {BeginExploit TeamNum Player Weapon State ?Dead}
  774.  
  775. [] endExploit(TeamNum Player) then
  776. {EndExploit TeamNum Player State}
  777. end
  778. end
  779.  
  780. InitPlayers = {MakeTuple players NbTeams}
  781. InitExploitations = {MakeTuple exploitations NbTeams}
  782. InitState = state(players: InitPlayers
  783. % The home is required for the UI
  784. home: Home
  785. tower: none
  786. % A near tower can kill a player
  787. nearTowers: nil
  788. visibleTowers: nil
  789. exploitations: InitExploitations)
  790. in
  791. for TeamNum in 1..NbTeams do
  792. InitPlayers.TeamNum = nil
  793. InitExploitations.TeamNum = nil
  794. end
  795.  
  796. {NewPort StateStream StatePort}
  797. {Send StatePort InitState}
  798. SquarePort = {NewPortObject HandleMsg InitState}
  799. end
  800.  
  801. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  802. % Game-specific functions
  803.  
  804. proc {CreateAllSquares Map Teams ?Squares ?Streams}
  805. NbRows = {Width Map}
  806. NbColumns = {Width Map.1}
  807. NbTeams = {Width Teams}
  808.  
  809. fun {GetHomeInfo X Y}
  810. fun {SearchHome TeamNum}
  811. if NbTeams < TeamNum then
  812. none
  813. elseif Teams.TeamNum.homePos.x == X andthen Teams.TeamNum.homePos.y == Y then
  814. home(owner:TeamNum port:Teams.TeamNum.home)
  815. else
  816. {SearchHome TeamNum+1}
  817. end
  818. end
  819. in
  820. if Map.Y.X \= home then
  821. none
  822. else
  823. {SearchHome 1}
  824. end
  825. end
  826. in
  827. Squares = {MakeTuple squares NbRows}
  828. Streams = {MakeTuple streams NbRows}
  829.  
  830. for RowNum in 1..NbRows do
  831. Squares.RowNum = {MakeTuple row NbColumns}
  832. Streams.RowNum = {MakeTuple row NbColumns}
  833.  
  834. for ColNum in 1..NbColumns do
  835. Pos = pos(x:ColNum y:RowNum)
  836. HomeInfo = {GetHomeInfo ColNum RowNum}
  837. in
  838. {CreateSquare Pos NbTeams HomeInfo Squares
  839. ?Squares.RowNum.ColNum
  840. ?Streams.RowNum.ColNum}
  841. end
  842. end
  843. end
  844.  
  845. % At the beginning of the game, there is two towers in diagonal of each home.
  846. proc {BuildInitialTowers Squares Team}
  847. NbRows = {Width Squares}
  848. NbCols = {Width Squares.1}
  849. in
  850. if Team.homePos.x < NbCols andthen 1 < Team.homePos.y then
  851. TowerX = Team.homePos.x + 1
  852. TowerY = Team.homePos.y - 1
  853. in
  854. {Send Squares.TowerY.TowerX buildTower(Team.num _)}
  855. end
  856.  
  857. if 1 < Team.homePos.x andthen Team.homePos.y < NbRows then
  858. TowerX = Team.homePos.x - 1
  859. TowerY = Team.homePos.y + 1
  860. in
  861. {Send Squares.TowerY.TowerX buildTower(Team.num _)}
  862. end
  863. end
  864.  
  865. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  866. % The Brain
  867.  
  868. proc {Embody Player Brain InitState}
  869. proc {AskBrain Env State}
  870. [Action NextState] = {Brain Env State}
  871. NextEnv
  872. Dead
  873. in
  874. {Send Player Action#?NextEnv#?Dead}
  875. {Wait NextEnv}
  876. {Wait Dead}
  877. if {Not Dead} then
  878. {AskBrain NextEnv NextState}
  879. end
  880. end
  881. in
  882. thread
  883. InitEnv
  884. Dead
  885. in
  886. % "noop" is actually useful
  887. {Send Player noop#?InitEnv#?Dead}
  888. {Wait InitEnv}
  889. {Wait Dead}
  890.  
  891. if {Not Dead} then
  892. {AskBrain InitEnv InitState}
  893. end
  894. end
  895. end
  896.  
  897. fun {CreateBrain StaticEnv Init}
  898. fun {$ Env State}
  899. if State == none then
  900. [build(player(none)) noop]
  901. else
  902. [move(Env.location) noop]
  903. end
  904. end
  905. end
  906.  
  907. % We are at CurLoc and we want to go to WantedLoc, what is the next location?
  908. fun {GetNextLoc CurLoc WantedLoc}
  909. X
  910. Y
  911. NextLoc = pos(x:X y:Y)
  912. in
  913. if WantedLoc.x < CurLoc.x then
  914. X = CurLoc.x - 1
  915. elseif CurLoc.x < WantedLoc.x then
  916. X = CurLoc.x + 1
  917. else
  918. X = CurLoc.x
  919. end
  920.  
  921. if WantedLoc.y < CurLoc.y then
  922. Y = CurLoc.y - 1
  923. elseif CurLoc.y < WantedLoc.y then
  924. Y = CurLoc.y + 1
  925. else
  926. Y = CurLoc.y
  927. end
  928.  
  929. NextLoc
  930. end
  931.  
  932. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  933. % GUI
  934.  
  935. % To be able to call methods asynchronously on an object
  936. fun {NewActive Class Init}
  937. Obj = {New Class Init}
  938. Port
  939. in
  940. thread Stream in
  941. {NewPort Stream Port}
  942. for Msg in Stream do {Obj Msg} end
  943. end
  944. proc {$ Msg} {Send Port Msg} end
  945. end
  946.  
  947. % "a", "b", "c", etc.
  948. fun {GetTeamID TeamNum}
  949. [&a + TeamNum - 1]
  950. end
  951.  
  952. % "A", "B", "C", etc.
  953. fun {GetTeamIDWeapon TeamNum}
  954. [&A + TeamNum - 1]
  955. end
  956.  
  957. class Gui
  958. attr
  959. resources
  960. squares
  961. grid
  962. normal
  963. field
  964. forest
  965. quarry
  966. mine
  967.  
  968. % W: width of the map
  969. meth init(Map NbTeams)
  970. Grid
  971. Resources = {MakeTuple resources NbTeams}
  972. TeamsInfo = {MakeTuple td NbTeams}
  973. W = {Width Map.1}
  974. H = {Width Map}
  975. in
  976. % Colors
  977. normal := white
  978. field := c(255 100 100)
  979. forest := c(135 75 20)
  980. quarry := c(180 180 180)
  981. mine := yellow
  982.  
  983. % Show resources of each team
  984. for TeamNum in 1..NbTeams do
  985. TeamsInfo.TeamNum = lr(label(text:"Team " # TeamNum # ": ")
  986. label(text:"" handle:Resources.TeamNum))
  987. end
  988.  
  989. % Create the window
  990. {{QTk.build td(
  991. grid(handle:Grid bg:white)
  992. lr(label(text:" Home " bg:black fg:white)
  993. label(text:" Food " bg:@field)
  994. label(text:" Wood " bg:@forest)
  995. label(text:" Stone " bg:@quarry)
  996. label(text:" Steel " bg:@mine))
  997. TeamsInfo
  998. button(text:"Quit" action:proc {$} {Application.exit 0} end)
  999. )} show}
  1000.  
  1001. % Configure the grid
  1002. for I in 1..H-1 do
  1003. {Grid configure(lrline column:1 columnspan:2*W-1 row:I*2 sticky:we)}
  1004. end
  1005. for I in 1..W-1 do
  1006. {Grid configure(tdline row:1 rowspan:2*H-1 column:I*2 sticky:ns)}
  1007. end
  1008. for I in 1..W do
  1009. {Grid columnconfigure(2*I-1 minsize:43)}
  1010. end
  1011. for I in 1..H do
  1012. {Grid rowconfigure(2*I-1 minsize:43)}
  1013. end
  1014.  
  1015. % Keep a reference to the widgets, so we can modify them later
  1016. grid := Grid
  1017. resources := Resources
  1018.  
  1019. {self createMap(Map)}
  1020. end
  1021.  
  1022. meth createMap(Map)
  1023. Squares = {MakeTuple squares {Width Map}}
  1024. in
  1025. for Y in {Arity Map} do
  1026. Squares.Y = {MakeTuple row {Width Map.Y}}
  1027.  
  1028. for X in {Arity Map.Y} do
  1029. Squares.Y.X = handle(properties:_ players:_)
  1030.  
  1031. case Map.Y.X
  1032. of normal then
  1033. {self addSquare(X Y @normal black Squares.Y.X)}
  1034. [] field then
  1035. {self addSquare(X Y @field black Squares.Y.X)}
  1036. [] forest then
  1037. {self addSquare(X Y @forest black Squares.Y.X)}
  1038. [] quarry then
  1039. {self addSquare(X Y @quarry black Squares.Y.X)}
  1040. [] mine then
  1041. {self addSquare(X Y @mine black Squares.Y.X)}
  1042. [] home then
  1043. {self addSquare(X Y black white Squares.Y.X)}
  1044. else
  1045. raise 'Unknown map element: ' # Map.Y.X end
  1046. end
  1047. end
  1048. end
  1049.  
  1050. squares := Squares
  1051. end
  1052.  
  1053. % X: horizontal location (the minimum is 1, the left)
  1054. % Y: vertical location (the minimum is 1, the top)
  1055. % Handle: to be able to modify the label located at (X,Y)
  1056. meth addSquare(X Y Bg Fg ?Handle)
  1057. {@grid configure(td(label(bg: Bg
  1058. fg: Fg
  1059. width: 5
  1060. height: 1
  1061. handle: Handle.properties)
  1062. label(bg: Bg
  1063. fg: Fg
  1064. width: 5
  1065. height: 2
  1066. wraplength: 38
  1067. handle: Handle.players))
  1068. row: 2*Y-1
  1069. column: 2*X-1)}
  1070. end
  1071.  
  1072. meth setResources(TeamNum Res)
  1073. {@resources.TeamNum set("food: " # Res.food #
  1074. " wood: " # Res.wood #
  1075. " stone: " # Res.stone #
  1076. " steel: " # Res.steel)}
  1077. end
  1078.  
  1079. meth setSquareProperties(X Y Home Tower)
  1080. StrHome
  1081. StrTower
  1082. in
  1083. if Home == none then
  1084. StrHome = ""
  1085. else
  1086. StrHome = "H" # {GetTeamID Home.owner} # " "
  1087. end
  1088.  
  1089. if Tower == none then
  1090. StrTower = ""
  1091. else
  1092. StrTower = "T" # {GetTeamID Tower.owner}
  1093. end
  1094.  
  1095. {@squares.Y.X.properties set(StrHome # StrTower)}
  1096. end
  1097.  
  1098. % 'Players' is a tuple: for each team, the list of the players
  1099. meth setSquarePlayers(X Y Players)
  1100. proc {CountPlayers List ?NbNormals ?NbWithWeapon}
  1101. proc {Step List NbNormalsAcc NbWithWeaponAcc}
  1102. case List
  1103. of nil then
  1104. NbNormals = NbNormalsAcc
  1105. NbWithWeapon = NbWithWeaponAcc
  1106. [] Player|OtherPlayers then
  1107. if {IsDet Player.weapon} then
  1108. {Step OtherPlayers NbNormalsAcc NbWithWeaponAcc+1}
  1109. else
  1110. {Step OtherPlayers NbNormalsAcc+1 NbWithWeaponAcc}
  1111. end
  1112. end
  1113. end
  1114. in
  1115. {Step List 0 0}
  1116. end
  1117.  
  1118. fun {AppendString CurStr StrToAppend}
  1119. Begin
  1120. in
  1121. if CurStr == "" then
  1122. Begin = ""
  1123. else
  1124. Begin = CurStr # " "
  1125. end
  1126.  
  1127. Begin # StrToAppend
  1128. end
  1129.  
  1130. fun {GetString TeamNum Str}
  1131. if {Width Players} < TeamNum then
  1132. Str
  1133. else
  1134. NbNormals
  1135. NbWithWeapon
  1136. StrNormals
  1137. StrWithWeapon
  1138. NewStr
  1139. in
  1140. {CountPlayers Players.TeamNum ?NbNormals ?NbWithWeapon}
  1141.  
  1142. if 0 < NbNormals then
  1143. StrNormals = NbNormals # {GetTeamID TeamNum}
  1144. else
  1145. StrNormals = ""
  1146. end
  1147.  
  1148. if 0 < NbWithWeapon then
  1149. StrWithWeapon = NbWithWeapon # {GetTeamIDWeapon TeamNum}
  1150. else
  1151. StrWithWeapon = ""
  1152. end
  1153.  
  1154. NewStr = {AppendString StrNormals StrWithWeapon}
  1155. {GetString TeamNum+1 {AppendString Str NewStr}}
  1156. end
  1157. end
  1158. in
  1159. {@squares.Y.X.players set({GetString 1 ""})}
  1160. end
  1161. end
  1162.  
  1163. proc {BindHome UI TeamNum ResStream}
  1164. thread
  1165. for Res in ResStream do
  1166. {UI setResources(TeamNum Res)}
  1167. end
  1168. end
  1169. end
  1170.  
  1171. proc {BindSquare UI X Y Stream}
  1172. thread
  1173. for Info in Stream do
  1174. {UI setSquarePlayers(X Y Info.players)}
  1175. {UI setSquareProperties(X Y Info.home Info.tower)}
  1176. end
  1177. end
  1178. end
  1179.  
  1180. % Generate the teams according to the number and locations of the homes in the map.
  1181. % Returns a tuple of teams, of the form:
  1182. % teams(team(num:1 home:Home homePos:pos(x:1 y:2))
  1183. fun {GenerateTeams Map UI}
  1184. NbRows = {Width Map}
  1185. NbCols = {Width Map.1}
  1186.  
  1187. fun {GetNextPos Pos}
  1188. if NbCols =< Pos.x then
  1189. pos(x:1 y:Pos.y+1)
  1190. else
  1191. pos(x:Pos.x+1 y:Pos.y)
  1192. end
  1193. end
  1194.  
  1195. fun {TraverseMap Pos Teams NbTeams}
  1196. if NbRows < Pos.y then
  1197. Teams
  1198. else
  1199. NextPos = {GetNextPos Pos}
  1200. in
  1201. if Map.(Pos.y).(Pos.x) == home then
  1202. Home
  1203. ResStream
  1204. TeamNum = NbTeams+1
  1205. NewTeam = team(num:TeamNum home:Home homePos:Pos)
  1206. in
  1207. {CreateHome Home ResStream}
  1208. {BindHome UI TeamNum ResStream}
  1209. {TraverseMap NextPos {Tuple.append Teams teams(NewTeam)} NbTeams+1}
  1210. else
  1211. {TraverseMap NextPos Teams NbTeams}
  1212. end
  1213. end
  1214. end
  1215. in
  1216. {TraverseMap pos(x:1 y:1) teams() 0}
  1217. end
  1218.  
  1219. % Get a map from a file
  1220.  
  1221. fun {GetMap Filename}
  1222. fun {GetSquareType Char}
  1223. case Char
  1224. of &- then normal
  1225. [] &M then mine
  1226. [] &W then forest
  1227. [] &Q then quarry
  1228. [] &H then home
  1229. [] &F then field
  1230. end
  1231. end
  1232.  
  1233. fun {CreateMap CharsList Map CurRow}
  1234. case CharsList
  1235.  
  1236. % End of the current row
  1237. of Char|NextChars andthen Char == &\n then
  1238. % Add the current row to the map
  1239. NewMap = {Tuple.append Map board(CurRow)}
  1240. in
  1241. {CreateMap NextChars NewMap row()}
  1242.  
  1243. % A square
  1244. [] Char|NextChars then
  1245. SquareType = {GetSquareType Char}
  1246. NewRow = {Tuple.append CurRow row(SquareType)}
  1247. in
  1248. {CreateMap NextChars Map NewRow}
  1249.  
  1250. % End of the map
  1251. [] nil then
  1252. if 0 < {Width CurRow} then
  1253. {Tuple.append Map board(CurRow)}
  1254. else
  1255. Map
  1256. end
  1257. end
  1258. end
  1259.  
  1260. File = {New Open.file init(name:Filename flags:[read])}
  1261. CharsList
  1262. in
  1263. {File read(list:CharsList size:all)}
  1264. {File close}
  1265. {CreateMap CharsList board() row()}
  1266. end
  1267.  
  1268. WorkingDir Teams UI NbTeams NbRows NbColumns Squares SquareStreams TeamA TeamB PlayerA PlayerB StaticEnvA BrainA StaticEnvB BrainB Map
  1269.  
  1270. in
  1271. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1272. % The "main function"
  1273.  
  1274. % Create the GUI object
  1275. WorkingDir = {OS.getCWD}
  1276. Map = {GetMap WorkingDir#'/map.txt'}
  1277.  
  1278. Teams = {GenerateTeams Map UI}
  1279. NbTeams = {Width Teams}
  1280. NbRows = {Width Map}
  1281. NbColumns = {Width Map.1}
  1282. UI = {NewActive Gui init(Map NbTeams)}
  1283.  
  1284. % Create the squares objects
  1285. {CreateAllSquares Map Teams ?Squares ?SquareStreams}
  1286.  
  1287. % Bind the squares
  1288. for RowNum in 1..NbRows do
  1289. for ColNum in 1..NbColumns do
  1290. {BindSquare UI ColNum RowNum SquareStreams.RowNum.ColNum}
  1291. end
  1292. end
  1293.  
  1294. % Initial towers for each team
  1295. for TeamNum in 1..{Width Teams} do
  1296. {BuildInitialTowers Squares Teams.TeamNum}
  1297. end
  1298.  
  1299. % Player Team A
  1300. TeamA = Teams.1
  1301. PlayerA = {CreatePlayer TeamA Squares Map StaticEnvA}
  1302.  
  1303. StaticEnvA = game(board: Map
  1304. teams: NbTeams
  1305. team: TeamA.num
  1306. goal: GOAL
  1307. home: TeamA.homePos)
  1308.  
  1309. BrainA = {CreateBrain StaticEnvA none}
  1310. {Embody PlayerA BrainA none}
  1311.  
  1312. % Player Team B
  1313. TeamB = Teams.2
  1314. PlayerB = {CreatePlayer TeamB Squares Map StaticEnvB}
  1315.  
  1316. StaticEnvB = game(board: Map
  1317. teams: NbTeams
  1318. team: TeamB.num
  1319. goal: GOAL
  1320. home: TeamB.homePos)
  1321.  
  1322. BrainB = {CreateBrain StaticEnvB none}
  1323. {Embody PlayerB BrainB none}
  1324. end
Add Comment
Please, Sign In to add comment