started tracking info to help collapse full enumeration

This commit is contained in:
Rudis Muiznieks 2014-07-27 00:43:32 -05:00
parent 1d0834e8ac
commit bd99018ed1
7 changed files with 110 additions and 33 deletions

View File

@ -1,8 +1,10 @@
namespace Ficdown.Parser.Tests namespace Ficdown.Parser.Tests
{ {
using System;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Player; using Player;
using ServiceStack.Text;
using TestStories; using TestStories;
using Xunit; using Xunit;
@ -20,6 +22,7 @@
var traverser = new GameTraverser(story); var traverser = new GameTraverser(story);
var test = traverser.Enumerate(); var test = traverser.Enumerate();
Console.WriteLine(test.Take(10).Dump());
} }
} }
} }

View File

@ -43,6 +43,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Model\Traverser\PageState.cs" /> <Compile Include="Model\Traverser\PageState.cs" />
<Compile Include="Model\Traverser\State.cs" />
<Compile Include="Model\Traverser\StateQueueItem.cs" />
<Compile Include="Player\IRenderer.cs" /> <Compile Include="Player\IRenderer.cs" />
<Compile Include="Player\HtmlRenderer.cs" /> <Compile Include="Player\HtmlRenderer.cs" />
<Compile Include="Parser\BlockHandler.cs" /> <Compile Include="Parser\BlockHandler.cs" />

View File

@ -8,10 +8,9 @@
internal class PageState internal class PageState
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public BitArray PlayerState { get; set; }
public BitArray ScenesSeen { get; set; }
public BitArray ActionsToShow { get; set; }
public Scene Scene { get; set; } public Scene Scene { get; set; }
public State State { get; set; }
public State AffectedState { get; set; }
public string Resolved { get; set; } public string Resolved { get; set; }
public IDictionary<string, string> Links { get; set; } public IDictionary<string, string> Links { get; set; }
@ -23,10 +22,11 @@
{ {
if (_uniqueHash == null) if (_uniqueHash == null)
{ {
var combined = new bool[PlayerState.Count + ScenesSeen.Count + ActionsToShow.Count]; var combined =
PlayerState.CopyTo(combined, 0); new bool[State.PlayerState.Count + State.ScenesSeen.Count + State.ActionsToShow.Count];
ScenesSeen.CopyTo(combined, PlayerState.Count); State.PlayerState.CopyTo(combined, 0);
ActionsToShow.CopyTo(combined, PlayerState.Count + ScenesSeen.Count); State.ScenesSeen.CopyTo(combined, State.PlayerState.Count);
State.ActionsToShow.CopyTo(combined, State.PlayerState.Count + State.ScenesSeen.Count);
var ba = new BitArray(combined); var ba = new BitArray(combined);
var byteSize = (int) Math.Ceiling(combined.Length/8.0); var byteSize = (int) Math.Ceiling(combined.Length/8.0);
var encoded = new byte[byteSize]; var encoded = new byte[byteSize];

View File

@ -0,0 +1,11 @@
namespace Ficdown.Parser.Model.Traverser
{
using System.Collections;
internal class State
{
public BitArray PlayerState { get; set; }
public BitArray ScenesSeen { get; set; }
public BitArray ActionsToShow { get; set; }
}
}

View File

@ -0,0 +1,10 @@
namespace Ficdown.Parser.Model.Traverser
{
using System.Collections.Generic;
internal class StateQueueItem
{
public PageState Page { get; set; }
public IList<State> AffectedStates { get; set; }
}
}

View File

@ -9,13 +9,13 @@
internal class GameTraverser internal class GameTraverser
{ {
private readonly StateManager _manager; private readonly StateManager _manager;
private readonly Queue<PageState> _processingQueue; private readonly Queue<StateQueueItem> _processingQueue;
private readonly IDictionary<string, PageState> _processed; private readonly IDictionary<string, PageState> _processed;
public GameTraverser(Story story) public GameTraverser(Story story)
{ {
_manager = new StateManager(story); _manager = new StateManager(story);
_processingQueue = new Queue<PageState>(); _processingQueue = new Queue<StateQueueItem>();
_processed = new Dictionary<string, PageState>(); _processed = new Dictionary<string, PageState>();
} }
@ -23,13 +23,18 @@
{ {
// generate comprehensive enumeration // generate comprehensive enumeration
_processingQueue.Enqueue(_manager.InitialState); var initial = _manager.InitialState;
_processingQueue.Enqueue(new StateQueueItem
{
Page = initial,
AffectedStates = new List<State> {initial.AffectedState}
});
while (_processingQueue.Count > 0) while (_processingQueue.Count > 0)
{ {
var state = _processingQueue.Dequeue(); var state = _processingQueue.Dequeue();
if (!_processed.ContainsKey(state.UniqueHash)) if (!_processed.ContainsKey(state.Page.UniqueHash))
{ {
_processed.Add(state.UniqueHash, state); _processed.Add(state.Page.UniqueHash, state.Page);
ProcessState(state); ProcessState(state);
} }
} }
@ -40,21 +45,39 @@
return _processed.Values; return _processed.Values;
} }
private void ProcessState(PageState currentState) private void ProcessState(StateQueueItem currentState)
{ {
var states = new HashSet<string>(); var states = new HashSet<string>();
foreach (
var anchor in var anchors = Utilities.ParseAnchors(currentState.Page.Scene.Description);
Utilities.ParseAnchors(currentState.Scene.Description) var conditionals =
.Where(a => a.Href.Target != null || a.Href.Toggles != null)) anchors.SelectMany(
a => a.Href.Conditions != null ? a.Href.Conditions.Select(c => c.Key) : new string[] {})
.Distinct()
.ToArray();
var hasFirstSeen = RegexLib.BlockQuotes.IsMatch(currentState.Page.Scene.Description);
foreach (var affected in currentState.AffectedStates)
{ {
var newState = _manager.ResolveNewState(anchor, currentState); // signal to previous scenes that this scene's used conditionals are important
if (!currentState.Links.ContainsKey(anchor.Original)) foreach (var conditional in conditionals) _manager.ToggleStateOn(affected, conditional);
currentState.Links.Add(anchor.Original, newState.UniqueHash);
// signal to previous scenes if this scene has first-seen text
if (hasFirstSeen) _manager.ToggleSeenSceneOn(affected, currentState.Page.Scene.Id);
}
foreach (var anchor in anchors.Where(a => a.Href.Target != null || a.Href.Toggles != null))
{
var newState = _manager.ResolveNewState(anchor, currentState.Page);
if (!currentState.Page.Links.ContainsKey(anchor.Original))
currentState.Page.Links.Add(anchor.Original, newState.UniqueHash);
if (!states.Contains(newState.UniqueHash) && !_processed.ContainsKey(newState.UniqueHash)) if (!states.Contains(newState.UniqueHash) && !_processed.ContainsKey(newState.UniqueHash))
{ {
states.Add(newState.UniqueHash); states.Add(newState.UniqueHash);
_processingQueue.Enqueue(newState); var newAffected = new List<State>(currentState.AffectedStates);
newAffected.Add(newState.AffectedState);
_processingQueue.Enqueue(new StateQueueItem {Page = newState, AffectedStates = newAffected});
} }
} }
} }

View File

@ -47,9 +47,18 @@
{ {
Id = Guid.Empty, Id = Guid.Empty,
Links = new Dictionary<string, string>(), Links = new Dictionary<string, string>(),
PlayerState = new BitArray(_stateMatrix.Keys.Count), State = new State
ScenesSeen = new BitArray(_sceneCount), {
ActionsToShow = new BitArray(_actionCount), PlayerState = new BitArray(_stateMatrix.Keys.Count),
ScenesSeen = new BitArray(_sceneCount),
ActionsToShow = new BitArray(_actionCount)
},
AffectedState = new State
{
PlayerState = new BitArray(_stateMatrix.Keys.Count),
ScenesSeen = new BitArray(_sceneCount),
ActionsToShow = new BitArray(_actionCount)
},
Scene = _story.Scenes[_story.FirstScene].Single(s => s.Conditions == null) Scene = _story.Scenes[_story.FirstScene].Single(s => s.Conditions == null)
}; };
} }
@ -59,23 +68,33 @@
{ {
var target = anchor.Href.Target ?? current.Scene.Key; var target = anchor.Href.Target ?? current.Scene.Key;
var newState = CloneState(current); var newState = ClonePage(current);
newState.ScenesSeen[current.Scene.Id - 1] = true; newState.State.ScenesSeen[current.Scene.Id - 1] = true;
if (anchor.Href.Toggles != null) if (anchor.Href.Toggles != null)
{ {
foreach (var toggle in anchor.Href.Toggles) foreach (var toggle in anchor.Href.Toggles)
{ {
if (_story.Actions.ContainsKey(toggle)) if (_story.Actions.ContainsKey(toggle))
{ {
newState.ActionsToShow[_story.Actions[toggle].Id - 1] = true; newState.State.ActionsToShow[_story.Actions[toggle].Id - 1] = true;
} }
newState.PlayerState[_stateMatrix[toggle]] = true; newState.State.PlayerState[_stateMatrix[toggle]] = true;
} }
} }
newState.Scene = GetScene(target, newState.PlayerState); newState.Scene = GetScene(target, newState.State.PlayerState);
return newState; return newState;
} }
public void ToggleStateOn(State state, string toggle)
{
state.PlayerState[_stateMatrix[toggle]] = true;
}
public void ToggleSeenSceneOn(State state, int sceneId)
{
state.ScenesSeen[sceneId - 1] = true;
}
private Scene GetScene(string target, BitArray playerState) private Scene GetScene(string target, BitArray playerState)
{ {
if (!_story.Scenes.ContainsKey(target)) if (!_story.Scenes.ContainsKey(target))
@ -102,15 +121,24 @@
return scene.Conditions.All(c => playerState[_stateMatrix[c.Key]] == c.Value); return scene.Conditions.All(c => playerState[_stateMatrix[c.Key]] == c.Value);
} }
private PageState CloneState(PageState state) private PageState ClonePage(PageState page)
{ {
return new PageState return new PageState
{ {
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
Links = new Dictionary<string, string>(), Links = new Dictionary<string, string>(),
PlayerState = state.PlayerState.Clone() as BitArray, State = new State
ScenesSeen = state.ScenesSeen.Clone() as BitArray, {
ActionsToShow = new BitArray(_actionCount) PlayerState = page.State.PlayerState.Clone() as BitArray,
ScenesSeen = page.State.ScenesSeen.Clone() as BitArray,
ActionsToShow = new BitArray(_actionCount)
},
AffectedState = new State
{
PlayerState = new BitArray(_stateMatrix.Keys.Count),
ScenesSeen = new BitArray(_sceneCount),
ActionsToShow = new BitArray(_actionCount)
}
}; };
} }
} }