Added support for conditional search expressions.

- Pattern: {type}{operator}{value}
-- Types: ar, cs, od, hp, bpm (length not yet implemented)
-- Operators: =/==, >, >=, <, <=

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han 2014-07-17 12:49:12 -04:00
parent 0a5e7f66ec
commit 4ecd50f488
3 changed files with 122 additions and 17 deletions

View File

@ -19,8 +19,12 @@
package itdelatrisu.opsu; package itdelatrisu.opsu;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* Indexed, expanding, doubly-linked list data type for song groups. * Indexed, expanding, doubly-linked list data type for song groups.
@ -43,6 +47,13 @@ public class OsuGroupList {
"Title", "Artist", "Creator", "BPM" "Title", "Artist", "Creator", "BPM"
}; };
/**
* Search pattern for conditional expressions.
*/
private static final Pattern SEARCH_CONDITION_PATTERN = Pattern.compile(
"(ar|cs|od|hp|bpm|length)(=|==|>|>=|<|<=)((\\d*\\.)?\\d+)"
);
/** /**
* List containing all parsed nodes. * List containing all parsed nodes.
*/ */
@ -261,30 +272,82 @@ public class OsuGroupList {
if (lastQuery != null && query.equals(lastQuery)) if (lastQuery != null && query.equals(lastQuery))
return false; return false;
lastQuery = query; lastQuery = query;
String[] terms = query.split("\\s+"); LinkedList<String> terms = new LinkedList<String>(Arrays.asList(query.split("\\s+")));
// if empty query, reset to original list // if empty query, reset to original list
if (query.isEmpty() || terms.length < 1) { if (query.isEmpty() || terms.isEmpty()) {
nodes = parsedNodes; nodes = parsedNodes;
return true; return true;
} }
// build list from first search term // find and remove any conditional search terms
nodes = new ArrayList<OsuGroupNode>(); LinkedList<String> condType = new LinkedList<String>();
for (OsuGroupNode node : parsedNodes) { LinkedList<String> condOperator = new LinkedList<String>();
if (node.matches(terms[0])) { LinkedList<Float> condValue = new LinkedList<Float>();
nodes.add(node);
continue; Iterator<String> termIter = terms.iterator();
while (termIter.hasNext()) {
String term = termIter.next();
Matcher m = SEARCH_CONDITION_PATTERN.matcher(term);
if (m.find()) {
condType.add(m.group(1));
condOperator.add(m.group(2));
condValue.add(Float.parseFloat(m.group(3)));
termIter.remove();
} }
} }
// remove nodes from list if they don't match all remaining terms // build an initial list from first search term
for (int i = 1; i < terms.length; i++) { nodes = new ArrayList<OsuGroupNode>();
Iterator<OsuGroupNode> iter = nodes.iterator(); if (terms.isEmpty()) {
while (iter.hasNext()) { // conditional term
OsuGroupNode node = iter.next(); String type = condType.remove();
if (!node.matches(terms[i])) String operator = condOperator.remove();
iter.remove(); float value = condValue.remove();
for (OsuGroupNode node : parsedNodes) {
if (node.matches(type, operator, value))
nodes.add(node);
}
} else {
// normal term
String term = terms.remove();
for (OsuGroupNode node : parsedNodes) {
if (node.matches(term))
nodes.add(node);
}
}
// iterate through remaining normal search terms
while (!terms.isEmpty()) {
if (nodes.isEmpty())
return true;
String term = terms.remove();
// remove nodes from list if they don't match all terms
Iterator<OsuGroupNode> nodeIter = nodes.iterator();
while (nodeIter.hasNext()) {
OsuGroupNode node = nodeIter.next();
if (!node.matches(term))
nodeIter.remove();
}
}
// iterate through remaining conditional terms
while (!condType.isEmpty()) {
if (nodes.isEmpty())
return true;
String type = condType.remove();
String operator = condOperator.remove();
float value = condValue.remove();
// remove nodes from list if they don't match all terms
Iterator<OsuGroupNode> nodeIter = nodes.iterator();
while (nodeIter.hasNext()) {
OsuGroupNode node = nodeIter.next();
if (!node.matches(type, operator, value))
nodeIter.remove();
} }
} }

View File

@ -214,4 +214,44 @@ public class OsuGroupNode implements Comparable<OsuGroupNode> {
return false; return false;
} }
/**
* Checks whether the node matches a given condition.
* @param type the condition type (ar, cs, od, hp, bpm, length)
* @param operator the operator (=/==, >, >=, <, <=)
* @param value the value
* @return true if the condition is met
*/
public boolean matches(String type, String operator, float value) {
for (OsuFile osu : osuFiles) {
// get value
float osuValue;
switch (type) {
case "ar": osuValue = osu.approachRate; break;
case "cs": osuValue = osu.circleSize; break;
case "od": osuValue = osu.overallDifficulty; break;
case "hp": osuValue = osu.HPDrainRate; break;
case "bpm": osuValue = osu.bpmMax; break;
// case "length": /* not implemented */ break;
default: return false;
}
// get operator
boolean met;
switch (operator) {
case "=":
case "==": met = (osuValue == value); break;
case ">": met = (osuValue > value); break;
case ">=": met = (osuValue >= value); break;
case "<": met = (osuValue < value); break;
case "<=": met = (osuValue <= value); break;
default: return false;
}
if (met)
return true;
}
return false;
}
} }

View File

@ -58,7 +58,7 @@ public class SongMenu extends BasicGameState {
/** /**
* Delay time, in milliseconds, between each search. * Delay time, in milliseconds, between each search.
*/ */
private static final int SEARCH_DELAY = 300; private static final int SEARCH_DELAY = 500;
/** /**
* Current start node (topmost menu entry). * Current start node (topmost menu entry).
@ -314,7 +314,9 @@ public class SongMenu extends BasicGameState {
else else
setFocus(Opsu.groups.getRandomNode(), -1, true); setFocus(Opsu.groups.getRandomNode(), -1, true);
} else { } else {
searchResultString = String.format("%d matches found!", Opsu.groups.size()); int size = Opsu.groups.size();
searchResultString = String.format("%d match%s found!",
size, (size == 1) ? "" : "es");
setFocus(Opsu.groups.getRandomNode(), -1, true); setFocus(Opsu.groups.getRandomNode(), -1, true);
} }
oldFocusNode = null; oldFocusNode = null;