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:
parent
0a5e7f66ec
commit
4ecd50f488
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user