This commit is contained in:
parent
7ae55fc727
commit
db24b58a6c
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c77b807962276e44693a3cd50bf4a8e6
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,18 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class AdditiveLoadScene : MonoBehaviour
|
||||
{
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 38488c7df686c5f4b84695bb26ac357a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -48,6 +48,19 @@ public class GameBoard : EntityBase, IAutoSerialize, IAutoDeserialize {
|
|||
[ReadOnly]
|
||||
public float collapseCount;
|
||||
|
||||
[Header("AI Settings")]
|
||||
[ReadOnly]
|
||||
public float aiDowntime = 0f;
|
||||
[Tooltip("Time it takes AI to think of move")]
|
||||
public float aiThinkTime = 1f;
|
||||
[Tooltip("Time it takes for AI to move between states")]
|
||||
public float aiAdvanceTime = 0.2f;
|
||||
[Tooltip("Time it takes for AI to move the cursor")]
|
||||
public float aiCursorMoveDelay = 0.1f;
|
||||
|
||||
public AIPhase aiPhase = AIPhase.None;
|
||||
public enum AIPhase { None, Thinking, LocationC, LocationB, LocationA, Drop }
|
||||
|
||||
void UpdateControls() {
|
||||
// Scan the board for tile activations
|
||||
board = board.Activate(out int activations);
|
||||
|
@ -60,19 +73,120 @@ public class GameBoard : EntityBase, IAutoSerialize, IAutoDeserialize {
|
|||
board = board.BreakPending(this, true);
|
||||
board = board.Collapse(out _);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
Collapse();
|
||||
comboDelay = 1.5f; // Half second combo delay
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
board = board.Collapse(out _);
|
||||
}
|
||||
|
||||
if (controlledByPlayer) {
|
||||
if (controlledByPlayer)
|
||||
PlayerControls();
|
||||
|
||||
if (controlledByAI)
|
||||
UpdateAI();
|
||||
}
|
||||
|
||||
|
||||
[ReadOnly]
|
||||
public int aiX, aiY;
|
||||
|
||||
void UpdateAI() {
|
||||
if (Recommended == null || !Recommended.isValid(board)) {
|
||||
SolveRecommendedMove();
|
||||
aiDowntime = aiThinkTime;
|
||||
aiPhase = AIPhase.Thinking;
|
||||
Debug.LogFormat("AI Move: {0},{1}", Recommended.location.x, Recommended.location.y);
|
||||
}
|
||||
|
||||
aiDowntime -= Time.deltaTime;
|
||||
|
||||
if (aiDowntime < 0) {
|
||||
if (aiPhase == AIPhase.Thinking) {
|
||||
aiPhase = AIPhase.LocationC;
|
||||
aiDowntime = aiAdvanceTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (aiPhase == AIPhase.LocationC) {
|
||||
var dir = CursorTowards(Recommended.locationC);
|
||||
|
||||
(aiX, aiY) = Recommended.locationC;
|
||||
|
||||
// FIXME: AI Cheats by restacking
|
||||
Current.first = MoveDir.None;
|
||||
Current.second = MoveDir.None;
|
||||
|
||||
if (dir == MoveDir.None && comboDelay <= 0) {
|
||||
aiPhase = AIPhase.LocationB;
|
||||
aiDowntime = aiAdvanceTime;
|
||||
}
|
||||
else {
|
||||
MoveCursor(dir);
|
||||
aiDowntime = aiCursorMoveDelay;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (aiPhase == AIPhase.LocationB) {
|
||||
var dir = CursorTowards(Recommended.locationB);
|
||||
(aiX, aiY) = Recommended.locationB;
|
||||
|
||||
if (dir == MoveDir.None) {
|
||||
aiPhase = AIPhase.LocationA;
|
||||
aiDowntime = aiAdvanceTime;
|
||||
}
|
||||
else {
|
||||
MoveCursor(dir);
|
||||
aiDowntime = aiCursorMoveDelay;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (aiPhase == AIPhase.LocationA) {
|
||||
var dir = CursorTowards(Recommended.location);
|
||||
(aiX, aiY) = Recommended.location;
|
||||
|
||||
if (dir == MoveDir.None) {
|
||||
aiPhase = AIPhase.Drop;
|
||||
aiDowntime = aiAdvanceTime;
|
||||
}
|
||||
else {
|
||||
MoveCursor(dir);
|
||||
aiDowntime = aiCursorMoveDelay;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (aiPhase == AIPhase.Drop) {
|
||||
Drop();
|
||||
aiPhase = AIPhase.None;
|
||||
Recommended = null;
|
||||
aiDowntime = aiAdvanceTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MoveDir CursorTowards((int x, int y) p) {
|
||||
var (x, y) = p;
|
||||
|
||||
if (x < cursorX)
|
||||
return MoveDir.Left;
|
||||
if (x > cursorX)
|
||||
return MoveDir.Right;
|
||||
|
||||
if (y > cursorY)
|
||||
return MoveDir.Up;
|
||||
if (y < cursorY)
|
||||
return MoveDir.Down;
|
||||
|
||||
return MoveDir.None;
|
||||
|
||||
}
|
||||
|
||||
public void MoveCursor(MoveDir d) {
|
||||
var (dx,dy) = d.ToPair();
|
||||
cursorX += dx;
|
||||
|
@ -147,10 +261,47 @@ public class GameBoard : EntityBase, IAutoSerialize, IAutoDeserialize {
|
|||
// AI Solver
|
||||
[ContextMenu("Recommend Move")]
|
||||
public void SolveRecommendedMove() {
|
||||
// For test validation:
|
||||
if (Current == null) {
|
||||
TestBoard();
|
||||
}
|
||||
|
||||
// First, create the sim board
|
||||
var simBoard = board.SelfCopy();
|
||||
|
||||
ValidateMoves();
|
||||
|
||||
var Options = new List<(Move m, BoardState result, int activations)>();
|
||||
foreach(var poss in PossibleMoves) {
|
||||
// Create a move with our pieces and possible location
|
||||
var potential = new Move() {
|
||||
location = poss.location,
|
||||
first = poss.first,
|
||||
second = poss.second,
|
||||
triplet = Current.triplet
|
||||
};
|
||||
|
||||
// Skip illegal placements
|
||||
if (!potential.isValid(simBoard)) continue;
|
||||
|
||||
var pBoard = simBoard.SelfCopy();
|
||||
pBoard = pBoard.Place(potential);
|
||||
pBoard = pBoard.SimulateRecursive(this, out int activations);
|
||||
|
||||
Options.Add((potential, pBoard, activations));
|
||||
}
|
||||
|
||||
Debug.LogFormat("Evaluated {0} Moves.", Options.Count);
|
||||
|
||||
var ordered =
|
||||
Options
|
||||
.OrderByDescending(o => o.activations) // First Priority, total attack size\
|
||||
.ThenBy(o => o.result.TallestStack()) // Lower columns
|
||||
.ThenByDescending(o => o.result.PrimedStacks()) // Setup future attacks
|
||||
.ThenByDescending(o => o.result.ExposedClusterSize()); // General bigness of moves
|
||||
//.ThenBy(o => o.result.TallestStack()); // Lower columns
|
||||
|
||||
Recommended = ordered.FirstOrDefault().m;
|
||||
}
|
||||
|
||||
static List<Move> PossibleMoves;
|
||||
|
@ -388,8 +539,12 @@ public class TileInfo {
|
|||
return this;
|
||||
}
|
||||
|
||||
public TileInfo SetPending() {
|
||||
public TileInfo SetPending(bool pending = true) {
|
||||
if (pending)
|
||||
detail |= TileDetail.Pending;
|
||||
else
|
||||
detail = detail & ~TileDetail.Pending;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -477,6 +632,41 @@ public static class BoardLogic {
|
|||
ts[row] = ti;
|
||||
}
|
||||
|
||||
public static int PrimedStacks(this BoardState bs) {
|
||||
int primedCount = 0;
|
||||
foreach(var col in bs.state) {
|
||||
var at = LowestAir(col);
|
||||
|
||||
if (at < 1) continue;
|
||||
|
||||
if (col[at].CombosWith(col[at - 1]))
|
||||
primedCount += 1;
|
||||
}
|
||||
|
||||
return primedCount;
|
||||
}
|
||||
|
||||
public static int LowestAir(this TileStack ts) {
|
||||
for(int y = 0; y < ts.Count; ++y) {
|
||||
if (ts[y].isAir) return y;
|
||||
}
|
||||
return ts.Count;
|
||||
}
|
||||
|
||||
public static int ExposedClusterSize(this BoardState bs) {
|
||||
Cluster whole = new Cluster();
|
||||
|
||||
for(int x = 0; x < bs.state.Count; ++x) {
|
||||
var col = bs[x];
|
||||
var at = col.LowestAir();
|
||||
if (at < 1) continue;
|
||||
|
||||
whole.AddRange(bs.Clusterize(x, at - 1));
|
||||
}
|
||||
|
||||
return whole.Distinct().Count();
|
||||
}
|
||||
|
||||
public static bool StackHasMatches(this TileStack ts) {
|
||||
// Check up the length of the column for a match 3
|
||||
for (int y = 0; y < ts.Count; ++y) {
|
||||
|
@ -666,6 +856,10 @@ public static class BoardLogic {
|
|||
|
||||
var (first, second, third) = m.triplet;
|
||||
|
||||
first.SetPending(false);
|
||||
second.SetPending(false);
|
||||
third.SetPending(false);
|
||||
|
||||
var (x, y) = m.location;
|
||||
//Debug.LogFormat("Placing at {0},{1}", x, y);
|
||||
|
||||
|
@ -864,7 +1058,7 @@ public class Move {
|
|||
if (loc.x < 0) return false;
|
||||
if (loc.x > mX) return false;
|
||||
if (loc.y < mY) return false;
|
||||
if (loc.y > mY + 3) return false;
|
||||
if (loc.y > mY + 2) return false;
|
||||
}
|
||||
|
||||
// Validate that the board positions are empty
|
||||
|
|
|
@ -8,7 +8,10 @@ EditorBuildSettings:
|
|||
- enabled: 1
|
||||
path: Assets/Scenes/MainMenuScene.unity
|
||||
guid: 259302c6bbed6234a948cfc4202af8f6
|
||||
- enabled: 1
|
||||
- enabled: 0
|
||||
path: Assets/Scenes/PrototypeScene.unity
|
||||
guid: 22fd93c991ecfed42a89b618f6cb386b
|
||||
- enabled: 1
|
||||
path: Assets/Playtest.unity
|
||||
guid: c77b807962276e44693a3cd50bf4a8e6
|
||||
m_configObjects: {}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
--- !u!30 &1
|
||||
GraphicsSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 12
|
||||
serializedVersion: 13
|
||||
m_Deferred:
|
||||
m_Mode: 1
|
||||
m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0}
|
||||
|
@ -31,6 +31,9 @@ GraphicsSettings:
|
|||
m_AlwaysIncludedShaders:
|
||||
- {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0}
|
||||
- {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0}
|
||||
- {fileID: 16000, guid: 0000000000000000f000000000000000, type: 0}
|
||||
- {fileID: 16001, guid: 0000000000000000f000000000000000, type: 0}
|
||||
- {fileID: 17000, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_PreloadedShaders: []
|
||||
m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000,
|
||||
type: 0}
|
||||
|
@ -55,3 +58,5 @@ GraphicsSettings:
|
|||
m_AlbedoSwatchInfos: []
|
||||
m_LightsUseLinearIntensity: 0
|
||||
m_LightsUseColorTemperature: 0
|
||||
m_LogWhenShaderIsCompiled: 0
|
||||
m_AllowEnlightenSupportForUpgradedProject: 1
|
||||
|
|
Loading…
Reference in New Issue