Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Failed Conditions
Push — main ( 2c9538...02418a )
by Dan
38s queued 18s
created

src/lib/Default/Plotter.php (1 issue)

Labels
Severity
1
<?php declare(strict_types=1);
2
3
use Smr\Exceptions\UserError;
4
use Smr\Path;
5
use Smr\PlotGroup;
6
use Smr\TransactionType;
7
8
class Plotter {
9
10
	public static function getX(PlotGroup $xType, int|string $X, int $gameID, AbstractSmrPlayer $player = null): mixed {
11
		// Special case for Location categories (i.e. Bar, HQ, SafeFed)
12
		if (!is_numeric($X)) {
13
			if ($xType != PlotGroup::Locations) {
14
				throw new Exception('Non-numeric X only exists for Locations');
15
			}
16
			return $X;
17
		}
18
19
		// In all other cases, X is a numeric ID
20
		$X = str2int($X);
0 ignored issues
show
The function str2int was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

20
		$X = /** @scrutinizer ignore-call */ str2int($X);
Loading history...
21
22
		// Helper function for plots to trade goods
23
		$getGoodWithTransaction = function(int $goodID) use ($xType, $player) {
24
			$good = Globals::getGood($goodID);
25
			if (isset($player) && !$player->meetsAlignmentRestriction($good['AlignRestriction'])) {
26
				throw new Exception('Player trying to access alignment-restricted good!');
27
			}
28
			$good['TransactionType'] = TransactionType::from(explode(' ', $xType->value)[0]);
29
			return $good;
30
		};
31
32
		return match ($xType) {
33
			PlotGroup::Technology => Globals::getHardwareTypes($X),
34
			PlotGroup::Ships => SmrShipType::get($X),
35
			PlotGroup::Weapons => SmrWeaponType::getWeaponType($X),
36
			PlotGroup::Locations => SmrLocation::getLocation($gameID, $X),
37
			PlotGroup::SellGoods, PlotGroup::BuyGoods => $getGoodWithTransaction($X),
38
			PlotGroup::Galaxies => SmrGalaxy::getGalaxy($gameID, $X), // $X is the galaxyID
39
		};
40
	}
41
42
	/**
43
	 * Returns the shortest path from $sector to $x as a Distance object.
44
	 * The path is guaranteed reversible ($x -> $sector == $sector -> $x), which
45
	 * is not true for findDistanceToX. If $x is not a SmrSector, then this
46
	 * function does 2x the work.
47
	 */
48
	public static function findReversiblePathToX(mixed $x, SmrSector $sector, bool $useFirst, AbstractSmrPlayer $needsToHaveBeenExploredBy = null, AbstractSmrPlayer $player = null): Path {
49
		if ($x instanceof SmrSector) {
50
51
			// To ensure reversibility, always plot lowest to highest.
52
			$reverse = $sector->getSectorID() > $x->getSectorID();
53
			if ($reverse) {
54
				$start = $x;
55
				$end = $sector;
56
			} else {
57
				$start = $sector;
58
				$end = $x;
59
			}
60
			$path = self::findDistanceToX($end, $start, $useFirst, $needsToHaveBeenExploredBy, $player);
61
			if ($path === false) {
62
				throw new UserError('Unable to plot from ' . $sector->getSectorID() . ' to ' . $x->getSectorID() . '.');
63
			}
64
			// Reverse if we plotted $x -> $sector (since we want $sector -> $x)
65
			if ($reverse) {
66
				$path->reversePath();
67
			}
68
69
		} else {
70
71
			// At this point we don't know what sector $x will be at
72
			$path = self::findDistanceToX($x, $sector, $useFirst, $needsToHaveBeenExploredBy, $player);
73
			if ($path === false) {
74
				throw new UserError('Unable to find what you\'re looking for, it either hasn\'t been added to this game or you haven\'t explored it yet.');
75
			}
76
			// Now that we know where $x is, make sure path is reversible
77
			// (i.e. start sector < end sector)
78
			if ($path->getEndSectorID() < $sector->getSectorID()) {
79
				$endSector = SmrSector::getSector($sector->getGameID(), $path->getEndSectorID());
80
				$path = self::findDistanceToX($sector, $endSector, true);
81
				$path->reversePath();
82
			}
83
84
		}
85
		return $path;
86
	}
87
88
	/**
89
	 * Returns the shortest path from $sector to $x as a Distance object.
90
	 * $x can be any type implemented by SmrSector::hasX or the string 'Distance'.
91
	 * The resulting path prefers neighbors in their order in SmrSector->links,
92
	 * (i.e. up, down, left, right).
93
	 *
94
	 * @return Smr\Path|array<int, array<int, Smr\Path>>|false
95
	 */
96
	public static function findDistanceToX(mixed $x, SmrSector $sector, bool $useFirst, AbstractSmrPlayer $needsToHaveBeenExploredBy = null, AbstractSmrPlayer $player = null, int $distanceLimit = 10000, int $lowLimit = 0, int $highLimit = 100000): Path|array|false {
97
		$warpAddIndex = TURNS_WARP_SECTOR_EQUIVALENCE - 1;
98
99
		$checkSector = $sector;
100
		$gameID = $sector->getGameID();
101
		$distances = [];
102
		$sectorsTravelled = 0;
103
		$visitedSectors = [];
104
		$visitedSectors[$checkSector->getSectorID()] = true;
105
		if ($x == 'Distance') {
106
			$distances[0][$checkSector->getSectorID()] = new Path($checkSector->getSectorID());
107
		}
108
109
		$distanceQ = [];
110
		for ($i = 0; $i <= TURNS_WARP_SECTOR_EQUIVALENCE; $i++) {
111
			$distanceQ[] = [];
112
		}
113
		//Warps first as a slight optimisation due to how visitedSectors is set.
114
		if ($checkSector->hasWarp() === true) {
115
			$d = new Path($checkSector->getSectorID());
116
			$d->addWarp($checkSector->getWarp());
117
			$distanceQ[$warpAddIndex][] = $d;
118
		}
119
		foreach ($checkSector->getLinks() as $nextSector) {
120
			$visitedSectors[$nextSector] = true;
121
			$d = new Path($checkSector->getSectorID());
122
			$d->addLink($nextSector);
123
			$distanceQ[0][] = $d;
124
		}
125
		$maybeWarps = 0;
126
		while ($maybeWarps <= TURNS_WARP_SECTOR_EQUIVALENCE) {
127
			$sectorsTravelled++;
128
			if ($sectorsTravelled > $distanceLimit) {
129
				return $distances;
130
			}
131
			if ($x == 'Distance') {
132
				$distances[$sectorsTravelled] = [];
133
			}
134
			$distanceQ[] = [];
135
			$q = array_shift($distanceQ);
136
			if (count($q) === 0) {
137
				$maybeWarps++;
138
				continue;
139
			}
140
			$maybeWarps = 0;
141
			while (($distance = array_shift($q)) !== null) {
142
				$checkSectorID = $distance->getEndSectorID();
143
				$visitedSectors[$checkSectorID] = true; // This is here for warps, because they are delayed visits if we set this before the actual visit we'll get sectors marked as visited long before they are actually visited - causes problems when it's quicker to walk to the warp exit than to warp there.
144
																// We still need to mark walked sectors as visited before we go to each one otherwise we get a huge number of paths being checked twice (up then left, left then up are essentially the same but if we set up-left as visited only when we actually check it then it gets queued up twice - nasty)
145
				if ($checkSectorID >= $lowLimit && $checkSectorID <= $highLimit) {
146
					$checkSector = SmrSector::getSector($gameID, $checkSectorID);
147
					if ($x == 'Distance') {
148
						$distances[$sectorsTravelled][$checkSector->getSectorID()] = $distance;
149
					} elseif (($needsToHaveBeenExploredBy === null || $needsToHaveBeenExploredBy->hasVisitedSector($checkSector->getSectorID())) === true
150
							&& $checkSector->hasX($x, $player) === true) {
151
						if ($useFirst === true) {
152
							return $distance;
153
						}
154
						$distances[$checkSector->getSectorID()] = $distance;
155
					}
156
					//Warps first as a slight optimisation due to how visitedSectors is set.
157
					if ($checkSector->hasWarp() === true) {
158
						if (!isset($visitedSectors[$checkSector->getWarp()])) {
159
							$cloneDistance = clone($distance);
160
							$cloneDistance->addWarp($checkSector->getWarp());
161
							$distanceQ[$warpAddIndex][] = $cloneDistance;
162
						}
163
					}
164
					foreach ($checkSector->getLinks() as $nextSector) {
165
						if (!isset($visitedSectors[$nextSector])) {
166
							$visitedSectors[$nextSector] = true;
167
168
							$cloneDistance = clone($distance);
169
							$cloneDistance->addLink($nextSector);
170
							$distanceQ[0][] = $cloneDistance;
171
						}
172
					}
173
				}
174
			}
175
		}
176
		if ($useFirst === true) {
177
			return false;
178
		}
179
		return $distances;
180
	}
181
182
	/**
183
	 * @param array<int, \SmrPort> $ports
184
	 * @param array<int, bool> $races
185
	 * @return array<int, array<int, Smr\Path>|false>
186
	 */
187
	public static function calculatePortToPortDistances(array $ports, array $races, int $distanceLimit = 10000, int $lowLimit = 0, int $highLimit = 100000): array {
188
		$distances = [];
189
		foreach ($ports as $port) {
190
			$sectorID = $port->getSectorID();
191
			if ($races[$port->getRaceID()] && $sectorID >= $lowLimit && $sectorID <= $highLimit) {
192
				$distances[$sectorID] = self::findDistanceToOtherPorts($port->getSector(), $distanceLimit, $lowLimit, $highLimit);
193
			}
194
		}
195
		return $distances;
196
	}
197
198
	/**
199
	 * @return array<int, Smr\Path>|false
200
	 */
201
	public static function findDistanceToOtherPorts(SmrSector $sector, int $distanceLimit = 10000, int $lowLimit = 0, int $highLimit = 100000): array|false {
202
		return self::findDistanceToX('Port', $sector, false, null, null, $distanceLimit, $lowLimit, $highLimit);
203
	}
204
205
}
206