started tracking info to help collapse full enumeration
This commit is contained in:
parent
1d0834e8ac
commit
bd99018ed1
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -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});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,9 +47,18 @@
|
||||||
{
|
{
|
||||||
Id = Guid.Empty,
|
Id = Guid.Empty,
|
||||||
Links = new Dictionary<string, string>(),
|
Links = new Dictionary<string, string>(),
|
||||||
|
State = new State
|
||||||
|
{
|
||||||
PlayerState = new BitArray(_stateMatrix.Keys.Count),
|
PlayerState = new BitArray(_stateMatrix.Keys.Count),
|
||||||
ScenesSeen = new BitArray(_sceneCount),
|
ScenesSeen = new BitArray(_sceneCount),
|
||||||
ActionsToShow = new BitArray(_actionCount),
|
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,
|
{
|
||||||
|
PlayerState = page.State.PlayerState.Clone() as BitArray,
|
||||||
|
ScenesSeen = page.State.ScenesSeen.Clone() as BitArray,
|
||||||
ActionsToShow = new BitArray(_actionCount)
|
ActionsToShow = new BitArray(_actionCount)
|
||||||
|
},
|
||||||
|
AffectedState = new State
|
||||||
|
{
|
||||||
|
PlayerState = new BitArray(_stateMatrix.Keys.Count),
|
||||||
|
ScenesSeen = new BitArray(_sceneCount),
|
||||||
|
ActionsToShow = new BitArray(_actionCount)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue