
org.cogchar.xploder.mgr.CursorManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.cogchar.lib.convoid Show documentation
Show all versions of org.cogchar.lib.convoid Show documentation
Cogchar "convoid" - (old) conversation content + state machines
/*
* Copyright 2011 by The Cogchar Project (www.cogchar.org).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cogchar.xploder.mgr;
import org.cogchar.xploder.cursors.IConvoidCursor;
import org.cogchar.xploder.cursors.MeaningScoreKeeper;
import org.cogchar.api.convoid.act.Category;
import org.cogchar.api.convoid.act.Step;
import org.cogchar.convoid.player.BehaviorContext;
import org.cogchar.convoid.player.BehaviorContext.Detail;
import org.cogchar.convoid.player.SpeechPlayer;
import org.cogchar.convoid.player.IBehaviorPlayable;
import org.cogchar.xploder.mgr.CursorRequest.BackupOption;
import org.cogchar.xploder.mgr.CursorRequest.ResetMode;
import org.cogchar.xploder.mgr.CursorRequest.ScoreMode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.logging.Logger;
import org.cogchar.convoid.broker.RemoteResponseFacade;
import org.cogchar.convoid.job.ChatJob;
import org.cogchar.convoid.job.ExpositionJob;
import org.cogchar.convoid.job.SpeechJob;
import org.cogchar.convoid.job.TalkJob;
import org.cogchar.platform.util.CollectionUtils;
import org.cogchar.platform.util.TimeUtils;
/**
*
* @author Matt Stevenson
*/
public class CursorManager {
private static Logger theLogger = Logger.getLogger(CursorManager.class.getName());
private static Random theRandomizer = new Random();
private static long theTopicKeepAliveTime = 10000L;
private Map myTypeManagerTable;
private List myManagers;
private List myCursors;
private SpeechJob myLastSpeechJob;
private SpeechJob myCurrentSpeechJob;
public CursorManager(Category rootCat) {
myTypeManagerTable = new HashMap();
myManagers = new ArrayList();
myCursors = new ArrayList();
buildAndRegisterManager(rootCat, "Expositions", new SpeechJobFactory("EXPOSITION", ExpositionJob.class, 0.9, 3000L), true);
buildAndRegisterManager(rootCat, "Chat", new SpeechJobFactory("CHAT", ChatJob.class, 0.9, 0L), true);
CursorGroup expo = myTypeManagerTable.get("EXPOSITION").getCursorGroup();
CursorGroup chat = myTypeManagerTable.get("CHAT").getCursorGroup();
expo.setBackupGroup(chat);
chat.setBackupGroup(expo);
loadExtraBehaviors(rootCat);
}
private void loadExtraBehaviors(Category rootCat){
Category extra = null;
for(Category c : rootCat.getSubCategories()){
if(c.getName().equals("EXTRA_BEHAVIORS")){
extra = c;
break;
}
}
if(extra == null){
return;
}
for(Category c : extra.getSubCategories()){
String n = c.getName();
buildAndRegisterManager(extra, n, new SpeechJobFactory(n, TalkJob.class, 0.9, 15000L), false);
}
}
public CursorGroup getCursorGroup(String type){
if(!myTypeManagerTable.containsKey(type)){
return null;
}
return myTypeManagerTable.get(type).getCursorGroup();
}
public BehaviorTypeManager getTypeManager(String type){
return myTypeManagerTable.get(type);
}
public void setLastPlayed(SpeechJob job){
if(job == null){
theLogger.info("Cannot set last played job to null!");
return;
}
String type = job.getCategoryCursor().getGroupType();
if(!myTypeManagerTable.containsKey(type)){
theLogger.info("Cannot set last played job with type: " + type);
return;
}
theLogger.info("Setting Last Played:\nType:" + type + " Cat: " + job.getCategoryName());
if(myCurrentSpeechJob != null){
myLastSpeechJob = myCurrentSpeechJob;
}
myCurrentSpeechJob = job;
}
public SpeechJob getLastPlayed(){
long time = TimeUtils.currentTimeMillis();
SpeechJob job = myCurrentSpeechJob;
if(myLastSpeechJob == null){
return job;
}
long dead = time - myLastSpeechJob.getUpdateStampMsec();
if(dead < theTopicKeepAliveTime){
job = myLastSpeechJob;
}
return job;
}
public BehaviorContext getMoreToSay(){
if(myCurrentSpeechJob == null){
return BehaviorContext.makeEmpty();
}
long time = TimeUtils.currentTimeMillis();
SpeechJob job = myCurrentSpeechJob;
if(myLastSpeechJob != null){
long dead = time - myLastSpeechJob.getUpdateStampMsec();
if(dead < theTopicKeepAliveTime){
job = myLastSpeechJob;
}
}
theLogger.info("Last Played: " + job.getCategoryName());
IConvoidCursor cc = job.getCategoryCursor();
if(cc.isFinishedAtTime(time)){
return BehaviorContext.makeEmpty().with(Detail.FROM_EXPO);
}
Step step = cc.getBestStepAtTime(time);
if(step != null){
IBehaviorPlayable player = new SpeechPlayer(step, job);
return new BehaviorContext().with(player).andActualType(cc.getGroupType());
}
addMeaningsForJob(job, 0.75, time);
return getOnTopicFromCursors(null, myCursors);
}
private BehaviorContext getOnTopicFromCursors(Map meanings, List cursors){
long time = TimeUtils.currentTimeMillis();
addMeaningsAtTime(meanings, 1.0, time);
Map scores = getScores(getCursorGroupTypes(), time);
if(scores.isEmpty()){
addMeaningAtTime("RANDOM", time);
scores = getScores(getCursorGroupTypes(), time);
}
IConvoidCursor best = MeaningScoreKeeper.getRandomBestMatchFromMeanings(
scores, cursors, meanings, time, 0.0);
if(best == null){
return BehaviorContext.makeEmpty();
}
SpeechJob job = null;
for(BehaviorTypeManager btm : myManagers){
job = btm.getCursorGroup().getJobForCursor(best);
if(job == null){
continue;
}
Step step = job.getCategoryCursor().getBestStep();
if(step == null){
return BehaviorContext.makeEmpty().withIntendedType(btm.getBehaviorType());
}
IBehaviorPlayable player = new SpeechPlayer(step, job);
return new BehaviorContext().with(player).andActualType(btm.getBehaviorType())
.andIntendedType(btm.getBehaviorType());
}
return BehaviorContext.makeEmpty();
}
public BehaviorContext getBehaviorContext(CursorRequest request){
Long time = request.getRequestTime();
if(request.getScoreMode() != ScoreMode.IGNORE){
addMeaningsAtTime(request.getMeanings(), 1.0, time);
if(request.getRequiredMeanings() != null){
for(String m : request.getRequiredMeanings()){
addMeaningAtTime(m, time);
}
}
}
FilteredCursorList cursorList = collectCursorsForRequest(request);
Map scores = getScores(request.getTypes(), time);
scores = filterScores(scores, cursorList.getCursors(), request.getScoreThreshold());
IConvoidCursor best = getRequestedCursor(request, cursorList.getPlayables(), scores);
BehaviorContext context = getCursorBehavior(best, time);
if(context.isEmptyBehavior() && request.getResetMode() != ResetMode.NONE){
context = getBehaviorToReplay(request, cursorList);
}
if(context.isEmptyBehavior()){
context = getBackupBehavior(request, cursorList);
}
return context;
}
private FilteredCursorList collectCursorsForRequest(CursorRequest request){
List cursors = getCursorsForTypes(request.getTypes());
return new FilteredCursorList(cursors, request);
}
private List getCursorsForTypes(List types){
List cursors = new ArrayList();
if(types == null){
return cursors;
}
for(String type : types){
CursorGroup group = getCursorGroup(type);
if(group != null){
cursors.addAll(group.getCursors());
}
}
return cursors;
}
private Map filterScores(Map scores,
List cursors, Double scoreThreshold){
if(scoreThreshold == null){
scoreThreshold = 0.0;
}
Map ret = new HashMap();
for(IConvoidCursor cc : cursors){
if(scores.containsKey(cc)){
double score = scores.get(cc);
if(score > scoreThreshold){
ret.put(cc, scores.get(cc));
}
}
}
return ret;
}
private Map getScores(Collection types, long time){
Map scores = new HashMap();
for(String t : types){
BehaviorTypeManager btm = getTypeManager(t);
scores.putAll(btm.getCursorGroup().getScoreKeeper().getScoresAtTime(time));
}
return scores;
}
private IConvoidCursor getRequestedCursor(CursorRequest request, List cursors,
Map scores){
List pscores = new ArrayList();
Map> passing = new HashMap();
for(IConvoidCursor a : cursors){
if(!scores.containsKey(a)){
continue;
}
double score = scores.get(a);
if(!passing.containsKey(score)){
passing.put(score, new ArrayList());
}
passing.get(score).add(a);
pscores.add(score);
}
ScoreMode mode = request.getScoreMode();
if(mode == ScoreMode.HIGH || mode == ScoreMode.LOW){
Collections.sort(pscores);
if(mode == ScoreMode.HIGH){
Collections.reverse(pscores);
}
}
if(!pscores.isEmpty()){
int max = 0;
switch(mode){
case IGNORE : max = pscores.size();
break;
case LOW :
case HIGH :
max = request.getChoices();
break;
}
max = Math.min(pscores.size(), max);
Integer rand = theRandomizer.nextInt(max);
List selected = passing.get(pscores.get(rand));
return selected.get(theRandomizer.nextInt(selected.size()));
}
return null;
}
private BehaviorContext getBehaviorToReplay(CursorRequest request, FilteredCursorList cursors){
Long time = request.getRequestTime();
ResetMode rmode = request.getResetMode();
IConvoidCursor best = getBestCursorToReset(request, cursors);
if(best == null){
return BehaviorContext.makeEmpty();
}
Long elapsed = time - best.getLastAdvanceTime();
if(rmode == ResetMode.TIMED && !pickBasedOnTime(elapsed)){
return BehaviorContext.makeEmpty();
}
best.resetAtTime(time);
return getCursorBehavior(best, time).with(Detail.RESET);
}
private IConvoidCursor getBestCursorToReset(CursorRequest request, FilteredCursorList cursors){
IConvoidCursor best = null;
double bestScore = 0.0;
for(IConvoidCursor a : cursors.getResetList()){
if(a.getLastAdvanceTime() == null){
continue;
}
long elapsed = request.getRequestTime() - a.getLastAdvanceTime();
Double newScore = 0.0;
for(Entry e : request.getMeanings().entrySet()){
if(a.getMeanings().contains(e.getKey())){
newScore += e.getValue();
}
}
newScore *= elapsed;
if(newScore > bestScore){
bestScore = newScore;
best = a;
}
}
return best;
}
private boolean pickBasedOnTime(long timeSpan){
//A logistic curve with 30 minutes at P(reset)=0.5
double secs = 1800;
double x = (double)(timeSpan/10.0);
double a = 3.6/secs;
double n = Math.pow(Math.E, (-a*(x-secs)));
double score = 1/(1.0 + n);
return theRandomizer.nextFloat() <= score;
}
private BehaviorContext getBackupBehavior(CursorRequest request, FilteredCursorList cursors){
List options = new ArrayList(request.getBackupOptions());
int len = options.size();
if(options == null || options.isEmpty()){
return BehaviorContext.makeEmpty();
}
BehaviorContext bc = BehaviorContext.makeEmpty();
int count = 0;
int offset = theRandomizer.nextInt(options.size());
do{
switch(options.get((count+offset)%len)){
case REMOTE : bc = RemoteResponseFacade.getResponseBehavior();break;
case RESET : bc = getBehaviorToReplay(request, cursors);break;
case RANDOM :
CursorRequest br = getBackupRequest(request);
br.getRequiredMeanings().add("RANDOM");
bc = getBehaviorContext(br).with(Detail.RANDOM);break;
}
count++;
if(count == len){
return bc;
}
}while(bc.isEmptyBehavior());
return bc;
}
public void killSpeechJob(SpeechJob job){
getCursorGroup(job.getCategoryCursor().getGroupType()).killSpeechJob(job);
}
private void buildAndRegisterManager(Category cat, String name, SpeechJobFactory factory, Boolean fetch){
if(myTypeManagerTable.containsKey(factory.getBehaviorType())){
theLogger.severe("Duplicate Group Types: " + name);
return;
}
BehaviorTypeManager manager = new BehaviorTypeManager(cat, name, factory, fetch);
if(!myTypeManagerTable.containsKey(factory.getBehaviorType())){
myTypeManagerTable.put(factory.getBehaviorType(), manager);
}
if(!myManagers.contains(manager)){
myManagers.add(manager);
}
myCursors.addAll(manager.getCursorGroup().getScoreKeeper().getCursors());
}
public void addMeaningAtTime(String meaning, long time){
if(meaning == null){
return;
}
for(BehaviorTypeManager btm : myManagers){
btm.getCursorGroup().getScoreKeeper().addMeaningAtTime(meaning, time);
}
}
public void addMeaningsAtTime(Map meanings, double perc, long time){
if(meanings == null || meanings.isEmpty()){
return;
}
for(BehaviorTypeManager btm : myManagers){
btm.getCursorGroup().getScoreKeeper().addMeaningsAtTime(meanings, perc, time);
}
}
public void addMeaningsForJob(SpeechJob job, double perc, long time){
if(job == null){
return;
}
Map meanings = new HashMap();
for(String m :job.getCategoryCursor().getActiveMeanings()){
meanings.put(m, 1.0);
}
addMeaningsAtTime(meanings, perc, time);
}
public boolean hasMeaning(String meaning){
for(BehaviorTypeManager btm : myManagers){
if(btm.getCursorGroup().getScoreKeeper().hasMeaning(meaning)){
return true;
}
}
return false;
}
public boolean containsMeanings(Set meanings){
for(String m : meanings){
if(hasMeaning(m)){
return true;
}
}
return false;
}
private Set getCursorGroupTypes(){
return myTypeManagerTable.keySet();
}
private BehaviorContext getCursorBehavior(IConvoidCursor cursor, long time){
if(cursor == null){
return BehaviorContext.makeEmpty();
}
CursorGroup cg = getCursorGroup(cursor.getGroupType());
if(cg == null){
return BehaviorContext.makeEmpty();
}
return cg.getCursorBehavior(cursor, time);
}
private CursorRequest getBackupRequest(CursorRequest orig){
CursorRequest request = new CursorRequest(orig);
request.setResetMode(ResetMode.NONE);
request.getBackupOptions().retainAll(CollectionUtils.list(BackupOption.REMOTE));
request.setMeanings(new HashMap());
return request;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy