PocketMine-MP 5.33.2 git-1133d49c924b4358c79d44eeb97dcbf56cb4d1eb
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 array_map;
31use function intdiv;
32use function min;
33
38
39 private const CAN_FLOW_DOWN = 1;
40 private const CAN_FLOW = 0;
41 private const BLOCKED = -1;
42
44 private array $flowCostVisited = [];
45
49 public function __construct(
50 private World $world,
51 private int $flowDecayPerBlock,
52 private \Closure $canFlowInto
53 ){}
54
55 private function calculateFlowCost(int $blockX, int $blockY, int $blockZ, int $accumulatedCost, int $maxCost, Facing $originOpposite, Facing $lastOpposite) : int{
56 $cost = 1000;
57
58 foreach(Facing::HORIZONTAL as $j){
59 if($j === $originOpposite || $j === $lastOpposite){
60 continue;
61 }
62 [$dx, $dy, $dz] = Facing::OFFSET[$j->value];
63 $x = $blockX + $dx;
64 $y = $blockY + $dy;
65 $z = $blockZ + $dz;
66
67 if(!isset($this->flowCostVisited[$hash = World::blockHash($x, $y, $z)])){
68 if(!$this->world->isInWorld($x, $y, $z) || !$this->canFlowInto($this->world->getBlockAt($x, $y, $z))){
69 $this->flowCostVisited[$hash] = self::BLOCKED;
70 }elseif($this->world->getBlockAt($x, $y - 1, $z)->canBeFlowedInto()){
71 $this->flowCostVisited[$hash] = self::CAN_FLOW_DOWN;
72 }else{
73 $this->flowCostVisited[$hash] = self::CAN_FLOW;
74 }
75 }
76
77 $status = $this->flowCostVisited[$hash];
78
79 if($status === self::BLOCKED){
80 continue;
81 }elseif($status === self::CAN_FLOW_DOWN){
82 return $accumulatedCost;
83 }
84
85 if($accumulatedCost >= $maxCost){
86 continue;
87 }
88
89 $realCost = $this->calculateFlowCost($x, $y, $z, $accumulatedCost + 1, $maxCost, $originOpposite, Facing::opposite($j));
90
91 if($realCost < $cost){
92 $cost = $realCost;
93 }
94 }
95
96 return $cost;
97 }
98
102 public function getOptimalFlowDirections(int $originX, int $originY, int $originZ) : array{
103 $flowCost = array_fill_keys(array_map(fn(Facing $f) => $f->value, Facing::HORIZONTAL), 1000);
104 $maxCost = intdiv(4, $this->flowDecayPerBlock);
105 foreach(Facing::HORIZONTAL as $j){
106 [$dx, $dy, $dz] = Facing::OFFSET[$j->value];
107 $x = $originX + $dx;
108 $y = $originY + $dy;
109 $z = $originZ + $dz;
110
111 if(!$this->world->isInWorld($x, $y, $z) || !$this->canFlowInto($this->world->getBlockAt($x, $y, $z))){
112 $this->flowCostVisited[World::blockHash($x, $y, $z)] = self::BLOCKED;
113 }elseif($this->world->getBlockAt($x, $y - 1, $z)->canBeFlowedInto()){
114 $this->flowCostVisited[World::blockHash($x, $y, $z)] = self::CAN_FLOW_DOWN;
115 $flowCost[$j->value] = $maxCost = 0;
116 }elseif($maxCost > 0){
117 $this->flowCostVisited[World::blockHash($x, $y, $z)] = self::CAN_FLOW;
118 $opposite = Facing::opposite($j);
119 $flowCost[$j->value] = $this->calculateFlowCost($x, $y, $z, 1, $maxCost, $opposite, $opposite);
120 $maxCost = min($maxCost, $flowCost[$j->value]);
121 }
122 }
123
124 $this->flowCostVisited = [];
125
126 $minCost = min($flowCost);
127
128 $isOptimalFlowDirection = [];
129
130 foreach($flowCost as $facing => $cost){
131 if($cost === $minCost){
132 $isOptimalFlowDirection[] = $facing;
133 }
134 }
135
136 return $isOptimalFlowDirection;
137 }
138
139 private function canFlowInto(Block $block) : bool{
140 return ($this->canFlowInto)($block);
141 }
142}
getOptimalFlowDirections(int $originX, int $originY, int $originZ)
__construct(private World $world, private int $flowDecayPerBlock, private \Closure $canFlowInto)