Still no inf SDF, incorrect SRS kicks, issues with incoming garbage and lots of shit

shit
This commit is contained in:
dan63047 2024-07-04 00:01:23 +03:00
parent f0d4d809a3
commit f5baf493ca
3 changed files with 263 additions and 9 deletions

View File

@ -123,6 +123,17 @@ class Board{
return true; return true;
} }
bool wasATSpin(Tetromino type, Coords coords, int rot){
if (!(positionIsValid(type, Coords(coords.x+1, coords.y), rot) ||
positionIsValid(type, Coords(coords.x-1, coords.y), rot) ||
positionIsValid(type, Coords(coords.x, coords.y+1), rot) ||
positionIsValid(type, Coords(coords.x, coords.y-1), rot))
){
return true;
}
return false;
}
void writeToBoard(Tetromino type, Coords coords, int rot) { void writeToBoard(Tetromino type, Coords coords, int rot) {
if (!positionIsValid(type, coords, rot)) throw Exception("Attempted to write $type to $coords in $rot rot"); if (!positionIsValid(type, coords, rot)) throw Exception("Attempted to write $type to $coords in $rot rot");
List<Coords> shape = shapes[type.index][rot]; List<Coords> shape = shapes[type.index][rot];
@ -131,6 +142,117 @@ class Board{
board[finalCoords.y][finalCoords.x] = type; board[finalCoords.y][finalCoords.x] = type;
} }
} }
void writeGarbage(GarbageData data, [int? amt]){
List<List<Tetromino>> garbage = [for (int i = 0; i < (amt??data.amt!); i++) [for (int k = 0; k < width; k++) k == data.column! ? Tetromino.empty : Tetromino.garbage]];
board.insertAll(0, garbage);
board.removeRange(height-garbage.length, height);
}
List<int> clearFullLines(){
int linesCleared = 0;
int garbageLines = 0;
int difficultLineClear = 0;
for (int i = 0; i < board.length; i++){
if (board[i-linesCleared].every((element) => element != Tetromino.empty)){
if (board[i-linesCleared].any((element) => element == Tetromino.garbage)) garbageLines++;
board.removeAt(i-linesCleared);
List<Tetromino> emptyRow = [for (int t = 0; t < width; t++) Tetromino.empty];
board.add(emptyRow);
linesCleared += 1;
}
}
if (linesCleared >= 4) difficultLineClear = 1;
return [linesCleared, garbageLines, difficultLineClear];
}
}
class IncomingGarbage{
int? frameOfThreat; // will enter board after this frame, null if unconfirmed
GarbageData data;
IncomingGarbage(this.data);
@override
String toString(){
return "f$frameOfThreat: col${data.column} amt${data.amt}";
}
void confirm(int confirmationFrame, int garbageSpeed){
frameOfThreat = confirmationFrame+garbageSpeed;
}
}
class LineClearResult{
int linesCleared;
Tetromino piece;
bool spin;
int garbageCleared;
int column;
int attackProduced;
LineClearResult(this.linesCleared, this.piece, this.spin, this.garbageCleared, this.column, this.attackProduced);
}
class Stats{
int combo = -1;
int btb = -1;
int attackRecived = 0;
int attackTanked = 0;
LineClearResult processLineClear(List<int> clearFullLinesResult, Tetromino current, Coords pos, bool spinWasLastMove, bool tspin){
if (clearFullLinesResult[0] > 0) combo++;
else combo = -1;
if (clearFullLinesResult[2] > 0) btb++;
else btb = -1;
int attack = 0;
switch (clearFullLinesResult[0]){
case 0:
if (spinWasLastMove && tspin) {
attack = garbage['t-spin']!;
}
break;
case 1:
if (spinWasLastMove && tspin) {
attack = garbage['t-spin single']!;
}else{
attack = garbage['single']!;
}
break;
case 2:
if (spinWasLastMove && tspin) {
attack = garbage['t-spin double']!;
}else{
attack = garbage['double']!;
}
break;
case 3:
if (spinWasLastMove && tspin) {
attack = garbage['t-spin triple']!;
}else{
attack = garbage['triple']!;
}
break;
case 4:
if (spinWasLastMove && tspin) {
attack = garbage['t-spin quad']!;
}else{
attack = garbage['quad']!;
}
break;
case 5:
if (spinWasLastMove && tspin) {
attack = garbage['t-spin penta']!;
}else{
attack = garbage['penta']!;
}
break;
case _:
developer.log("${clearFullLinesResult[0]} lines cleared");
break;
}
return LineClearResult(clearFullLinesResult[0], Tetromino.empty, false, clearFullLinesResult[1], 0, attack);
}
} }
class Simulation{ class Simulation{
@ -150,9 +272,13 @@ void main() async {
int currentFrame = 0; int currentFrame = 0;
events.removeAt(0); // get rig of Event.start events.removeAt(0); // get rig of Event.start
Event nextEvent = events.removeAt(0); Event nextEvent = events.removeAt(0);
Stats stats = Stats();
Board board = Board(10, 20, 20); Board board = Board(10, 20, 20);
KicksetBase kickset = SRSPlus();
List<IncomingGarbage> garbageQueue = [];
Tetromino? hold; Tetromino? hold;
int rot = 0; int rot = 0;
bool spinWasLastMove = false;
Coords coords = Coords(3, 21); Coords coords = Coords(3, 21);
double gravityBucket = 0.00000000000000; double gravityBucket = 0.00000000000000;
@ -180,6 +306,20 @@ void main() async {
return queue.removeAt(0); return queue.removeAt(0);
} }
bool handleRotation(int r){
if (r == 0) return true;
int future_rotation = (rot + r) % 4;
List<Coords> tests = (current == Tetromino.I ? kickset.kickTableI : kickset.kickTable)[rot][r == -1 ? 0 : r];
for (Coords test in tests){
if (board.positionIsValid(current, coords+test, future_rotation)){
coords += test;
rot = future_rotation;
return true;
}
}
return false;
}
coords += spawnPositionFixes[current.index]; coords += spawnPositionFixes[current.index];
for (currentFrame; currentFrame <= replay.roundLengths[0]; currentFrame++){ for (currentFrame; currentFrame <= replay.roundLengths[0]; currentFrame++){
gravityBucket += settings != null ? (handling!.sdfActive ? settings.g! * settings.handling!.sdf : settings.g!) : 0; gravityBucket += settings != null ? (handling!.sdfActive ? settings.g! * settings.handling!.sdf : settings.g!) : 0;
@ -194,7 +334,7 @@ void main() async {
} }
if (board.positionIsValid(current, Coords(coords.x, coords.y-gravityImpact), rot)) coords.y -= gravityImpact; if (board.positionIsValid(current, Coords(coords.x, coords.y-gravityImpact), rot)) coords.y -= gravityImpact;
print("$currentFrame: $current at $coords\n$board"); print("$currentFrame: $current at $coords\n$board");
//print(stats.combo);
if (currentFrame == nextEvent.frame){ if (currentFrame == nextEvent.frame){
while (currentFrame == nextEvent.frame){ while (currentFrame == nextEvent.frame){
print("Processing $nextEvent"); print("Processing $nextEvent");
@ -212,13 +352,13 @@ void main() async {
activeKeypresses[nextEvent.data.key] = nextEvent; activeKeypresses[nextEvent.data.key] = nextEvent;
switch (nextEvent.data.key){ switch (nextEvent.data.key){
case KeyType.rotateCCW: case KeyType.rotateCCW:
rot = (rot-1)%4; handleRotation(-1);
break; break;
case KeyType.rotateCW: case KeyType.rotateCW:
rot = (rot+1)%4; handleRotation(1);
break; break;
case KeyType.rotate180: case KeyType.rotate180:
rot = (rot+2)%4; handleRotation(2);
break; break;
case KeyType.moveLeft: case KeyType.moveLeft:
case KeyType.moveRight: case KeyType.moveRight:
@ -231,9 +371,42 @@ void main() async {
case KeyType.hardDrop: case KeyType.hardDrop:
coords.y = sonicDrop(); coords.y = sonicDrop();
board.writeToBoard(current, coords, rot); board.writeToBoard(current, coords, rot);
bool tspin = board.wasATSpin(current, coords, rot);
LineClearResult lineClear = stats.processLineClear(board.clearFullLines(), current, coords, spinWasLastMove, tspin);
print("${lineClear.linesCleared} lines, ${lineClear.garbageCleared} garbage");
if (garbageQueue.isNotEmpty) {
if (lineClear.linesCleared > 0){
int garbageToDelete = lineClear.attackProduced;
for (IncomingGarbage garbage in garbageQueue){
if (garbage.data.amt! >= garbageToDelete) {
garbageToDelete -= garbage.data.amt!;
lineClear.attackProduced = garbageToDelete;
garbage.data.amt = 0;
}else{
garbage.data.amt = garbage.data.amt! - garbageToDelete;
lineClear.attackProduced = 0;
break;
}
}
}else{
int needToTake = settings!.garbageCap!;
for (IncomingGarbage garbage in garbageQueue){
if ((garbage.frameOfThreat??99999999) > currentFrame) break;
if (garbage.data.amt! > needToTake) {
board.writeGarbage(garbage.data, needToTake);
garbage.data.amt = garbage.data.amt! - needToTake;
break;
} else {
board.writeGarbage(garbage.data);
needToTake -= garbage.data.amt!;
garbage.data.amt = 0;
}
}
}
garbageQueue.removeWhere((element) => element.data.amt == 0);
}
current = getNewOne(); current = getNewOne();
coords = Coords(3, 21) + spawnPositionFixes[current.index]; coords = Coords(3, 21) + spawnPositionFixes[current.index];
//handling!.movementKeyReleased(true, true);
case KeyType.hold: case KeyType.hold:
switch (hold){ switch (hold){
case null: case null:
@ -247,6 +420,7 @@ void main() async {
current = temp; current = temp;
break; break;
} }
rot = 0;
coords = Coords(3, 21) + spawnPositionFixes[current.index]; coords = Coords(3, 21) + spawnPositionFixes[current.index];
break; break;
case KeyType.chat: case KeyType.chat:
@ -258,6 +432,11 @@ void main() async {
default: default:
developer.log(nextEvent.data.key.name); developer.log(nextEvent.data.key.name);
} }
if (nextEvent.data.key == KeyType.rotateCW && nextEvent.data.key == KeyType.rotateCW && nextEvent.data.key == KeyType.rotateCW){
spinWasLastMove = true;
}else{
spinWasLastMove = false;
}
break; break;
case EventType.keyup: case EventType.keyup:
nextEvent as EventKeyPress; nextEvent as EventKeyPress;
@ -292,6 +471,22 @@ void main() async {
case EventType.end: case EventType.end:
currentFrame = replay.roundLengths[0]+1; currentFrame = replay.roundLengths[0]+1;
break; break;
case EventType.ige:
nextEvent as EventIGE;
switch (nextEvent.data.data.type){
case "interaction":
garbageQueue.add(IncomingGarbage((nextEvent.data.data as IGEdataInteraction).data));
case "interaction_confirm":
GarbageData data = (nextEvent.data.data as IGEdataInteraction).data;
if(data.type != "targeted") garbageQueue.firstWhere((element) => element.data.iid == data.iid).confirm(currentFrame, settings!.garbageSpeed!);
case "allow_targeting":
case "target":
default:
developer.log("Unknown IGE type: ${nextEvent.data.type}", level: 900);
break;
}
// garbage speed counts from interaction comfirm
break;
default: default:
developer.log("Event wasn't processed: ${nextEvent}", level: 900); developer.log("Event wasn't processed: ${nextEvent}", level: 900);
} }

View File

@ -590,6 +590,7 @@ class DataFullOptions{
gMargin = json["gmargin"]; gMargin = json["gmargin"];
gIncrease = json["gincrease"]; gIncrease = json["gincrease"];
garbageMultiplier = json["garbagemultiplier"].toDouble(); garbageMultiplier = json["garbagemultiplier"].toDouble();
garbageCap = json["garbagecap"];
garbageCapIncrease = json["garbagecapincrease"].toDouble(); garbageCapIncrease = json["garbagecapincrease"].toDouble();
garbageCapMax = json["garbagecapmax"]; garbageCapMax = json["garbagecapmax"];
garbageHoleSize = json["garbageholesize"]; garbageHoleSize = json["garbageholesize"];
@ -815,9 +816,9 @@ List<List<List<Coords>>> shapes = [
[Coords(0, 1), Coords(1, 0), Coords(1, 1), Coords(1, 2)] [Coords(0, 1), Coords(1, 0), Coords(1, 1), Coords(1, 2)]
] ]
]; ];
List<Coords> spawnPositionFixes = [Coords(0, 0), Coords(0, 0), Coords(2, 1), Coords(0, 0), Coords(0, -1), Coords(0, 0), Coords(0, 0)]; List<Coords> spawnPositionFixes = [Coords(0, 0), Coords(0, 0), Coords(1, 1), Coords(0, 0), Coords(0, -1), Coords(0, 0), Coords(0, 0)];
const Map<String, double> garbage = { const Map<String, int> garbage = {
"single": 0, "single": 0,
"double": 1, "double": 1,
"triple": 2, "triple": 2,
@ -840,3 +841,61 @@ double comboBonus = 0.25;
int comboMinifier = 1; int comboMinifier = 1;
double comboMinifierLog = 1.25; double comboMinifierLog = 1.25;
List<int> comboTable = [0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5]; List<int> comboTable = [0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5];
class KicksetBase {
List<Coords>? additionalOffsets;
late List<Coords> additionalOffsetEmpty;
List<int>? spawnRotation;
late List<List<List<Coords>>> kickTable; //kickTable[initRot][rotDirection-1][kick]
late List<List<List<Coords>>> kickTableI;
}
class SRSPlus extends KicksetBase{
SRSPlus(){
kickTable = [
[
[Coords( 0, 0), Coords( 1, 0), Coords( 1, 1), Coords( 0,-2), Coords( 1,-2)], // 0 -> 270
[Coords( 0, 0), Coords(-1, 0), Coords(-1, 1), Coords( 0,-2), Coords(-1,-2)], // 0 -> 90
[Coords( 0, 0), Coords(-1, 0), Coords(-1, 1), Coords( 0,-2), Coords(-1,-2)], // 0 -> 180
],
[
[Coords( 0, 0), Coords( 1, 0), Coords( 1,-1), Coords( 0, 2), Coords( 1, 2)], // 90 -> 0
[Coords( 0, 0), Coords( 1, 0), Coords( 1,-1), Coords( 0, 2), Coords( 1, 2)], // 90 -> 180
[Coords( 0, 0), Coords( 1, 0), Coords( 1,-1), Coords( 0, 2), Coords( 1, 2)], // 90 -> 270
],
[
[Coords( 0, 0), Coords(-1, 0), Coords(-1, 1), Coords( 0,-2), Coords(-1,-2)], // 180 -> 90
[Coords( 0, 0), Coords( 1, 0), Coords( 1, 1), Coords( 0,-2), Coords( 1,-2)], // 180 -> 270
[Coords( 0, 0), Coords( 1, 0), Coords( 1, 1), Coords( 0,-2), Coords( 1,-2)], // 180 -> 0
],
[
[Coords( 0, 0), Coords(-1, 0), Coords(-1,-1), Coords( 0, 2), Coords(-1, 2)], // 270 -> 180
[Coords( 0, 0), Coords(-1, 0), Coords(-1,-1), Coords( 0, 2), Coords(-1, 2)], // 270 -> 0
[Coords( 0, 0), Coords(-1, 0), Coords(-1,-1), Coords( 0, 2), Coords(-1, 2)], // 270 -> 90
]
];
kickTableI = [
[
[Coords( 0, 0), Coords( 1, 0), Coords( 1, 1), Coords( 0,-2), Coords( 1,-2)], // 0 -> 270
[Coords( 0, 0), Coords(-1, 0), Coords(-1, 1), Coords( 0,-2), Coords(-1,-2)], // 0 -> 90
[Coords( 0, 0), Coords( 1, 0), Coords( 1, 1), Coords( 0,-2), Coords( 1,-2)], // 0 -> 180
],
[
[Coords( 0, 0), Coords( 1, 0), Coords( 1,-1), Coords( 0, 2), Coords( 1, 2)], // 90 -> 0
[Coords( 0, 0), Coords( 1, 0), Coords( 1,-1), Coords( 0, 2), Coords( 1, 2)], // 90 -> 180
[Coords( 0, 0), Coords( 1, 0), Coords( 1, 1), Coords( 0,-2), Coords( 1,-2)], // 90 -> 270
],
[
[Coords( 0, 0), Coords(-1, 0), Coords(-1, 1), Coords( 0,-2), Coords(-1,-2)], // 180 -> 90
[Coords( 0, 0), Coords( 1, 0), Coords( 1, 1), Coords( 0,-2), Coords( 1,-2)], // 180 -> 270
[Coords( 0, 0), Coords(-1, 0), Coords(-1, 1), Coords( 0,-2), Coords(-1,-2)], // 180 -> 0
],
[
[Coords( 0, 0), Coords(-1, 0), Coords(-1,-1), Coords( 0, 2), Coords(-1, 2)], // 270 -> 180
[Coords( 0, 0), Coords(-1, 0), Coords(-1,-1), Coords( 0, 2), Coords(-1, 2)], // 270 -> 0
[Coords( 0, 0), Coords(-1, 0), Coords(-1,-1), Coords( 0, 2), Coords(-1, 2)], // 270 -> 90
]
];
additionalOffsetEmpty = [Coords( 0, 0),Coords( 0, 0),Coords( 0, 0),Coords( 0, 0)];
}
}

View File

@ -1184,7 +1184,7 @@ class _TwoRecordsThingy extends StatelessWidget {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
for (int i = 1; i < sprintStream.records.length; i++) ListTile( for (int i = 1; i < blitzStream.records.length; i++) ListTile(
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SingleplayerRecordView(record: blitzStream.records[i]))), onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SingleplayerRecordView(record: blitzStream.records[i]))),
leading: Text("#${i+1}", style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9) ), leading: Text("#${i+1}", style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9) ),
title: Text("${NumberFormat.decimalPattern().format(blitzStream.records[i].endContext.score)} points", title: Text("${NumberFormat.decimalPattern().format(blitzStream.records[i].endContext.score)} points",