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
{
using System;
using System.Linq;
using System.Text;
using Player;
using ServiceStack.Text;
using TestStories;
using Xunit;
@ -20,6 +22,7 @@
var traverser = new GameTraverser(story);
var test = traverser.Enumerate();
Console.WriteLine(test.Take(10).Dump());
}
}
}

View File

@ -43,6 +43,8 @@
</ItemGroup>
<ItemGroup>
<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\HtmlRenderer.cs" />
<Compile Include="Parser\BlockHandler.cs" />

View File

@ -8,10 +8,9 @@
internal class PageState
{
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 State State { get; set; }
public State AffectedState { get; set; }
public string Resolved { get; set; }
public IDictionary<string, string> Links { get; set; }
@ -23,10 +22,11 @@
{
if (_uniqueHash == null)
{
var combined = new bool[PlayerState.Count + ScenesSeen.Count + ActionsToShow.Count];
PlayerState.CopyTo(combined, 0);
ScenesSeen.CopyTo(combined, PlayerState.Count);
ActionsToShow.CopyTo(combined, PlayerState.Count + ScenesSeen.Count);
var combined =
new bool[State.PlayerState.Count + State.ScenesSeen.Count + State.ActionsToShow.Count];
State.PlayerState.CopyTo(combined, 0);
State.ScenesSeen.CopyTo(combined, State.PlayerState.Count);
State.ActionsToShow.CopyTo(combined, State.PlayerState.Count + State.ScenesSeen.Count);
var ba = new BitArray(combined);
var byteSize = (int) Math.Ceiling(combined.Length/8.0);
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
{
private readonly StateManager _manager;
private readonly Queue<PageState> _processingQueue;
private readonly Queue<StateQueueItem> _processingQueue;
private readonly IDictionary<string, PageState> _processed;
public GameTraverser(Story story)
{
_manager = new StateManager(story);
_processingQueue = new Queue<PageState>();
_processingQueue = new Queue<StateQueueItem>();
_processed = new Dictionary<string, PageState>();
}
@ -23,13 +23,18 @@
{
// 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)
{
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);
}
}
@ -40,21 +45,39 @@
return _processed.Values;
}
private void ProcessState(PageState currentState)
private void ProcessState(StateQueueItem currentState)
{
var states = new HashSet<string>();
foreach (
var anchor in
Utilities.ParseAnchors(currentState.Scene.Description)
.Where(a => a.Href.Target != null || a.Href.Toggles != null))
var anchors = Utilities.ParseAnchors(currentState.Page.Scene.Description);
var conditionals =
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);
if (!currentState.Links.ContainsKey(anchor.Original))
currentState.Links.Add(anchor.Original, newState.UniqueHash);
// signal to previous scenes that this scene's used conditionals are important
foreach (var conditional in conditionals) _manager.ToggleStateOn(affected, conditional);
// 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))
{
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,
Links = new Dictionary<string, string>(),
PlayerState = new BitArray(_stateMatrix.Keys.Count),
ScenesSeen = new BitArray(_sceneCount),
ActionsToShow = new BitArray(_actionCount),
State = new State
{
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)
};
}
@ -59,23 +68,33 @@
{
var target = anchor.Href.Target ?? current.Scene.Key;
var newState = CloneState(current);
newState.ScenesSeen[current.Scene.Id - 1] = true;
var newState = ClonePage(current);
newState.State.ScenesSeen[current.Scene.Id - 1] = true;
if (anchor.Href.Toggles != null)
{
foreach (var toggle in anchor.Href.Toggles)
{
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;
}
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)
{
if (!_story.Scenes.ContainsKey(target))
@ -102,15 +121,24 @@
return scene.Conditions.All(c => playerState[_stateMatrix[c.Key]] == c.Value);
}
private PageState CloneState(PageState state)
private PageState ClonePage(PageState page)
{
return new PageState
{
Id = Guid.NewGuid(),
Links = new Dictionary<string, string>(),
PlayerState = state.PlayerState.Clone() as BitArray,
ScenesSeen = state.ScenesSeen.Clone() as BitArray,
ActionsToShow = new BitArray(_actionCount)
State = new State
{
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)
}
};
}
}