package aps; @:structInit class ItemPlacement { public var x:Int; public var y:Int; public var w:Int; public var h:Int; public function new(_x:Int, _y:Int, _w:Int, _h:Int) { x = _x; y = _y; w = _w; h = _h; } public function isInsideBounds(_bounds:ItemPlacement):Bool { if (w < 1 || h < 1 || _bounds.w < 1 || _bounds.h < 1) { trace("isInsideBounds: placement.w / h or _bounds.w / h is less than 1"); return false; } return (x >= _bounds.x && y >= _bounds.y && x + w <= _bounds.x + _bounds.w && y + h <= _bounds.y + _bounds.h); } } class Inventetris { public final cols:Int; public final rows:Int; public final placements:Array; public final occupiedMap:Array>; public var isNoFreeCell(default, null):Bool; public function new(_cols:Int, _rows:Int) { if (_cols < 1) _cols = 1; if (_rows < 1) _rows = 1; cols = _cols; rows = _rows; placements = []; occupiedMap = [ for (iy in 0...rows) [ for (ix in 0...cols) null ] ]; isNoFreeCell = false; // trace(w, h); } function clearOccupiedMap():Void { for (iy in 0...rows) { for (ix in 0...cols) { occupiedMap[iy][ix] = null; } } } public function clear():Void { clearOccupiedMap(); placements.resize(0); isNoFreeCell = false; } function _clearRect(_x:Int, _y:Int, _w:Int, _h:Int, _isSecondStep = false):Array { final itemsToBeDeleted:Array = []; final x1 = _x < 0 ? 0 : _x; final y1 = _y < 0 ? 0 : _y; var x2 = _x + _w; if (x2 > cols) x2 = cols; var y2 = _y + _h; if (y2 > rows) y2 = rows; for (iy in y1...y2) { for (ix in x1...x2) { final removedItem = occupiedMap[iy][ix]; occupiedMap[iy][ix] = null; if (removedItem != null) { if (!itemsToBeDeleted.contains(removedItem)) itemsToBeDeleted.push(removedItem); if (!_isSecondStep) { for (item in _clearRect(removedItem.x, removedItem.y, removedItem.w, removedItem.h, true)) { if (!itemsToBeDeleted.contains(item)) itemsToBeDeleted.push(item); } } } } } return itemsToBeDeleted; } public function clearRect(_x:Int, _y:Int, _w:Int, _h:Int):Array { final itemsToBeDeleted = _clearRect(_x, _y, _w, _h); for (item in itemsToBeDeleted) { placements.remove(item); } return itemsToBeDeleted; } public function deletePlacement(_ip:ItemPlacement):Void { clearRect(_ip.x, _ip.y, _ip.w, _ip.h); } public function isPlacementFits(_ip:ItemPlacement):Bool { if (!isPlacementInside(_ip)) { return false; } else { for (iy in _ip.y..._ip.y + _ip.h) { for (ix in _ip.x..._ip.x + _ip.w) { if (occupiedMap[iy][ix] != null) return false; } } return true; } } public function tryPost(_allowRotate:Bool, _ip:ItemPlacement, _isSecondAttempt = false):Bool { if (_ip.w < 1) _ip.w = 1; if (_ip.h < 1) _ip.h = 1; if (placements.contains(_ip)) { return false; } else { if (!isPlacementFits(_ip)) { if (_isSecondAttempt || findFreeSpaceForPlacement(_allowRotate, _ip) == null) return false; else return tryPost(_allowRotate, _ip, true); } else { placements.push(_ip); for (iy in _ip.y..._ip.y + _ip.h) { for (ix in _ip.x..._ip.x + _ip.w) { occupiedMap[iy][ix] = _ip; } } var atleastOneFree = false; for (iy in 0...rows) { for (ix in 0...cols) { if (occupiedMap[iy][ix] == null) { atleastOneFree = true; // trace(occupiedMap); break; } } if (atleastOneFree) break; } isNoFreeCell = !atleastOneFree; // if (isNoFreeCell) trace("full"); return true; } } } function isPlacementInside(_ip:ItemPlacement):Bool { return _ip.isInsideBounds({ _x: 0, _y: 0, _w: cols, _h: rows }); } function findFreeSpaceForPlacement(_allowRotate:Bool, _ip:ItemPlacement):ItemPlacement { final minSide = Std.int(Math.min(_ip.w, _ip.h)); if (minSide > cols && minSide > rows) return null; function findObstaclesAndGetX(_tx:Int, _ty:Int, _tw:Int, _th:Int):Int { var x2 = _tx + _tw; if (x2 > cols) return cols; var y2 = _ty + _th; if (y2 > rows) return _tx; for (iy in _ty...y2) { for (ix in _tx...x2) { // trace(ix, iy); if (occupiedMap[iy][ix] != null) return ix; } } return -1; } for (carriageY in 0...rows - minSide + 1) { var carriageX = 0; while (carriageX < cols - minSide + 1) { // trace('carriage: ${carriageX}, ${carriageY}'); var obstacleX = findObstaclesAndGetX(carriageX, carriageY, _ip.w, _ip.h); if (_allowRotate && obstacleX > -1 && occupiedMap[carriageY][carriageX] == null) { // trace("attempt to rotate"); obstacleX = findObstaclesAndGetX(carriageX, carriageY, _ip.h, _ip.w); if (obstacleX < 0) { // trace("rotation succeeded"); final w = _ip.w; _ip.w = _ip.h; _ip.h = w; } } if (obstacleX > -1) { carriageX = obstacleX + 1; } else { _ip.x = carriageX; _ip.y = carriageY; // trace('fits here: ${_ip}'); return _ip; } } } return null; } }