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]
|
[ReadOnly]
|
||||||
public float collapseCount;
|
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() {
|
void UpdateControls() {
|
||||||
// Scan the board for tile activations
|
// Scan the board for tile activations
|
||||||
board = board.Activate(out int activations);
|
board = board.Activate(out int activations);
|
||||||
|
@ -60,19 +73,120 @@ public class GameBoard : EntityBase, IAutoSerialize, IAutoDeserialize {
|
||||||
board = board.BreakPending(this, true);
|
board = board.BreakPending(this, true);
|
||||||
board = board.Collapse(out _);
|
board = board.Collapse(out _);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
Collapse();
|
Collapse();
|
||||||
comboDelay = 1.5f; // Half second combo delay
|
comboDelay = 1.5f; // Half second combo delay
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
board = board.Collapse(out _);
|
board = board.Collapse(out _);
|
||||||
|
}
|
||||||
|
|
||||||
if (controlledByPlayer) {
|
if (controlledByPlayer)
|
||||||
PlayerControls();
|
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) {
|
public void MoveCursor(MoveDir d) {
|
||||||
var (dx,dy) = d.ToPair();
|
var (dx,dy) = d.ToPair();
|
||||||
cursorX += dx;
|
cursorX += dx;
|
||||||
|
@ -147,10 +261,47 @@ public class GameBoard : EntityBase, IAutoSerialize, IAutoDeserialize {
|
||||||
// AI Solver
|
// AI Solver
|
||||||
[ContextMenu("Recommend Move")]
|
[ContextMenu("Recommend Move")]
|
||||||
public void SolveRecommendedMove() {
|
public void SolveRecommendedMove() {
|
||||||
|
// For test validation:
|
||||||
|
if (Current == null) {
|
||||||
|
TestBoard();
|
||||||
|
}
|
||||||
|
|
||||||
// First, create the sim board
|
// First, create the sim board
|
||||||
var simBoard = board.SelfCopy();
|
var simBoard = board.SelfCopy();
|
||||||
|
|
||||||
ValidateMoves();
|
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;
|
static List<Move> PossibleMoves;
|
||||||
|
@ -388,8 +539,12 @@ public class TileInfo {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TileInfo SetPending() {
|
public TileInfo SetPending(bool pending = true) {
|
||||||
|
if (pending)
|
||||||
detail |= TileDetail.Pending;
|
detail |= TileDetail.Pending;
|
||||||
|
else
|
||||||
|
detail = detail & ~TileDetail.Pending;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -477,6 +632,41 @@ public static class BoardLogic {
|
||||||
ts[row] = ti;
|
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) {
|
public static bool StackHasMatches(this TileStack ts) {
|
||||||
// Check up the length of the column for a match 3
|
// Check up the length of the column for a match 3
|
||||||
for (int y = 0; y < ts.Count; ++y) {
|
for (int y = 0; y < ts.Count; ++y) {
|
||||||
|
@ -666,6 +856,10 @@ public static class BoardLogic {
|
||||||
|
|
||||||
var (first, second, third) = m.triplet;
|
var (first, second, third) = m.triplet;
|
||||||
|
|
||||||
|
first.SetPending(false);
|
||||||
|
second.SetPending(false);
|
||||||
|
third.SetPending(false);
|
||||||
|
|
||||||
var (x, y) = m.location;
|
var (x, y) = m.location;
|
||||||
//Debug.LogFormat("Placing at {0},{1}", x, y);
|
//Debug.LogFormat("Placing at {0},{1}", x, y);
|
||||||
|
|
||||||
|
@ -864,7 +1058,7 @@ public class Move {
|
||||||
if (loc.x < 0) return false;
|
if (loc.x < 0) return false;
|
||||||
if (loc.x > mX) return false;
|
if (loc.x > mX) return false;
|
||||||
if (loc.y < mY) 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
|
// Validate that the board positions are empty
|
||||||
|
|
|
@ -8,7 +8,10 @@ EditorBuildSettings:
|
||||||
- enabled: 1
|
- enabled: 1
|
||||||
path: Assets/Scenes/MainMenuScene.unity
|
path: Assets/Scenes/MainMenuScene.unity
|
||||||
guid: 259302c6bbed6234a948cfc4202af8f6
|
guid: 259302c6bbed6234a948cfc4202af8f6
|
||||||
- enabled: 1
|
- enabled: 0
|
||||||
path: Assets/Scenes/PrototypeScene.unity
|
path: Assets/Scenes/PrototypeScene.unity
|
||||||
guid: 22fd93c991ecfed42a89b618f6cb386b
|
guid: 22fd93c991ecfed42a89b618f6cb386b
|
||||||
|
- enabled: 1
|
||||||
|
path: Assets/Playtest.unity
|
||||||
|
guid: c77b807962276e44693a3cd50bf4a8e6
|
||||||
m_configObjects: {}
|
m_configObjects: {}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
--- !u!30 &1
|
--- !u!30 &1
|
||||||
GraphicsSettings:
|
GraphicsSettings:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
serializedVersion: 12
|
serializedVersion: 13
|
||||||
m_Deferred:
|
m_Deferred:
|
||||||
m_Mode: 1
|
m_Mode: 1
|
||||||
m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0}
|
m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0}
|
||||||
|
@ -31,6 +31,9 @@ GraphicsSettings:
|
||||||
m_AlwaysIncludedShaders:
|
m_AlwaysIncludedShaders:
|
||||||
- {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0}
|
- {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0}
|
||||||
- {fileID: 10770, 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_PreloadedShaders: []
|
||||||
m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000,
|
m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000,
|
||||||
type: 0}
|
type: 0}
|
||||||
|
@ -55,3 +58,5 @@ GraphicsSettings:
|
||||||
m_AlbedoSwatchInfos: []
|
m_AlbedoSwatchInfos: []
|
||||||
m_LightsUseLinearIntensity: 0
|
m_LightsUseLinearIntensity: 0
|
||||||
m_LightsUseColorTemperature: 0
|
m_LightsUseColorTemperature: 0
|
||||||
|
m_LogWhenShaderIsCompiled: 0
|
||||||
|
m_AllowEnlightenSupportForUpgradedProject: 1
|
||||||
|
|
Loading…
Reference in New Issue