From 4ecd50f48853317a9e8e76f1696ec45863995ff1 Mon Sep 17 00:00:00 2001 From: Jeffrey Han Date: Thu, 17 Jul 2014 12:49:12 -0400 Subject: [PATCH] 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 --- src/itdelatrisu/opsu/OsuGroupList.java | 93 +++++++++++++++++++---- src/itdelatrisu/opsu/OsuGroupNode.java | 40 ++++++++++ src/itdelatrisu/opsu/states/SongMenu.java | 6 +- 3 files changed, 122 insertions(+), 17 deletions(-) diff --git a/src/itdelatrisu/opsu/OsuGroupList.java b/src/itdelatrisu/opsu/OsuGroupList.java index 6e0fc796..f3a840be 100644 --- a/src/itdelatrisu/opsu/OsuGroupList.java +++ b/src/itdelatrisu/opsu/OsuGroupList.java @@ -19,8 +19,12 @@ package itdelatrisu.opsu; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; 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. @@ -43,6 +47,13 @@ public class OsuGroupList { "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. */ @@ -261,30 +272,82 @@ public class OsuGroupList { if (lastQuery != null && query.equals(lastQuery)) return false; lastQuery = query; - String[] terms = query.split("\\s+"); + LinkedList terms = new LinkedList(Arrays.asList(query.split("\\s+"))); // if empty query, reset to original list - if (query.isEmpty() || terms.length < 1) { + if (query.isEmpty() || terms.isEmpty()) { nodes = parsedNodes; return true; } - // build list from first search term - nodes = new ArrayList(); - for (OsuGroupNode node : parsedNodes) { - if (node.matches(terms[0])) { - nodes.add(node); - continue; + // find and remove any conditional search terms + LinkedList condType = new LinkedList(); + LinkedList condOperator = new LinkedList(); + LinkedList condValue = new LinkedList(); + + Iterator 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 - for (int i = 1; i < terms.length; i++) { - Iterator iter = nodes.iterator(); - while (iter.hasNext()) { - OsuGroupNode node = iter.next(); - if (!node.matches(terms[i])) - iter.remove(); + // build an initial list from first search term + nodes = new ArrayList(); + if (terms.isEmpty()) { + // conditional term + String type = condType.remove(); + String operator = condOperator.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 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 nodeIter = nodes.iterator(); + while (nodeIter.hasNext()) { + OsuGroupNode node = nodeIter.next(); + if (!node.matches(type, operator, value)) + nodeIter.remove(); } } diff --git a/src/itdelatrisu/opsu/OsuGroupNode.java b/src/itdelatrisu/opsu/OsuGroupNode.java index d59b57ae..bf12f5c5 100644 --- a/src/itdelatrisu/opsu/OsuGroupNode.java +++ b/src/itdelatrisu/opsu/OsuGroupNode.java @@ -214,4 +214,44 @@ public class OsuGroupNode implements Comparable { 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; + } } \ No newline at end of file diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index 9ae9ec78..bea220a6 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -58,7 +58,7 @@ public class SongMenu extends BasicGameState { /** * 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). @@ -314,7 +314,9 @@ public class SongMenu extends BasicGameState { else setFocus(Opsu.groups.getRandomNode(), -1, true); } 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); } oldFocusNode = null;