PocketMine-MP 5.21.2 git-b2aa6396c3cc2cafdd815eacc360e1ad89599899
Loading...
Searching...
No Matches
MinimumCostFlowCalculator.php
1<?php
2
3/*
4 *
5 * ____ _ _ __ __ _ __ __ ____
6 * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7 * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8 * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9 * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10 *
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * @author PocketMine Team
17 * @link http://www.pocketmine.net/
18 *
19 *
20 */
21
22declare(strict_types=1);
23
24namespace pocketmine\block\utils;
25
29use function array_fill_keys;
30use function intdiv;
31use function min;
32
37
38 private const CAN_FLOW_DOWN = 1;
39 private const CAN_FLOW = 0;
40 private const BLOCKED = -1;
41
43 private array $flowCostVisited = [];
44
48 public function __construct(
49 private World $world,
50 private int $flowDecayPerBlock,
51 private \Closure $canFlowInto
52 ){}
53
54 private function calculateFlowCost(int $blockX, int $blockY, int $blockZ, int $accumulatedCost, int $maxCost, int $originOpposite, int $lastOpposite) : int{
55 $cost = 1000;
56
57 foreach(Facing::HORIZONTAL as $j){
58 if($j === $originOpposite || $j === $lastOpposite){
59 continue;
60 }
61 [$dx, $dy, $dz] = Facing::OFFSET[$j];
62 $x = $blockX + $dx;
63 $y = $blockY + $dy;
64 $z = $blockZ + $dz;
65
66 if(!isset($this->flowCostVisited[$hash = World::blockHash($x, $y, $z)])){
67 if(!$this->world->isInWorld($x, $y, $z) || !$this->canFlowInto($this->world->getBlockAt($x, $y, $z))){
68 $this->flowCostVisited[$hash] = self::BLOCKED;
69 }elseif($this->world->getBlockAt($x, $y - 1, $z)->canBeFlowedInto()){
70 $this->flowCostVisited[$hash] = self::CAN_FLOW_DOWN;
71 }else{
72 $this->flowCostVisited[$hash] = self::CAN_FLOW;
73 }
74 }
75
76 $status = $this->flowCostVisited[$hash];
77
78 if($status === self::BLOCKED){
79 continue;
80 }elseif($status === self::CAN_FLOW_DOWN){
81 return $accumulatedCost;
82 }
83
84 if($accumulatedCost >= $maxCost){
85 continue;
86 }
87
88 $realCost = $this->calculateFlowCost($x, $y, $z, $accumulatedCost + 1, $maxCost, $originOpposite, Facing::opposite($j));
89
90 if($realCost < $cost){
91 $cost = $realCost;
92 }
93 }
94
95 return $cost;
96 }
97
101 public function getOptimalFlowDirections(int $originX, int $originY, int $originZ) : array{
102 $flowCost = array_fill_keys(Facing::HORIZONTAL, 1000);
103 $maxCost = intdiv(4, $this->flowDecayPerBlock);
104 foreach(Facing::HORIZONTAL as $j){
105 [$dx, $dy, $dz] = Facing::OFFSET[$j];
106 $x = $originX + $dx;
107 $y = $originY + $dy;
108 $z = $originZ + $dz;
109
110 if(!$this->world->isInWorld($x, $y, $z) || !$this->canFlowInto($this->world->getBlockAt($x, $y, $z))){
111 $this->flowCostVisited[World::blockHash($x, $y, $z)] = self::BLOCKED;
112 }elseif($this->world->getBlockAt($x, $y - 1, $z)->canBeFlowedInto()){
113 $this->flowCostVisited[World::blockHash($x, $y, $z)] = self::CAN_FLOW_DOWN;
114 $flowCost[$j] = $maxCost = 0;
115 }elseif($maxCost > 0){
116 $this->flowCostVisited[World::blockHash($x, $y, $z)] = self::CAN_FLOW;
117 $opposite = Facing::opposite($j);
118 $flowCost[$j] = $this->calculateFlowCost($x, $y, $z, 1, $maxCost, $opposite, $opposite);
119 $maxCost = min($maxCost, $flowCost[$j]);
120 }
121 }
122
123 $this->flowCostVisited = [];
124
125 $minCost = min($flowCost);
126
127 $isOptimalFlowDirection = [];
128
129 foreach($flowCost as $facing => $cost){
130 if($cost === $minCost){
131 $isOptimalFlowDirection[] = $facing;
132 }
133 }
134
135 return $isOptimalFlowDirection;
136 }
137
138 private function canFlowInto(Block $block) : bool{
139 return ($this->canFlowInto)($block);
140 }
141}
getOptimalFlowDirections(int $originX, int $originY, int $originZ)
__construct(private World $world, private int $flowDecayPerBlock, private \Closure $canFlowInto)