Libraryless. Click here for Pure Java version (2720L/18K/58K).
1 | !759 |
2 | |
3 | static Map<S, L<S>> thescripts = synchroTreeMap(); |
4 | |
5 | static class PerUser { |
6 | S dialogID; |
7 | ScriptState scriptState; |
8 | } |
9 | |
10 | static class ScriptState { |
11 | S scriptID; |
12 | long position; |
13 | |
14 | *(S *scriptID) {} |
15 | *() {} |
16 | } |
17 | |
18 | static new Map<S, PerUser> perUserMap; |
19 | static new ThreadLocal<PerUser> puCurrent; |
20 | static new ThreadLocal<ScriptState> scriptStateCurrent; |
21 | |
22 | static new L<S> snippetIDs; |
23 | |
24 | static O mainBot; |
25 | |
26 | p { |
27 | load("snippetIDs"); |
28 | |
29 | reload(); |
30 | |
31 | load("perUserMap"); |
32 | makeBot("A Dialog Bot"); |
33 | } |
34 | |
35 | // does not increase the index |
36 | static FC parseFunctionCall() { |
37 | ret parseFunctionCall(currentScriptLine()); |
38 | } |
39 | |
40 | static S currentScriptLine() { |
41 | int l = l(thescript()); |
42 | int idx = (int) (scriptState().position % l); |
43 | ret thescript().get(idx); |
44 | } |
45 | |
46 | synchronized answer { |
47 | // init per-user stuff |
48 | |
49 | S dialogID = cast call(mainBot, "getDialogID"); |
50 | |
51 | if "get dialog id" { |
52 | ret structure(dialogID); |
53 | } |
54 | |
55 | if "dialog engine: number of users" { |
56 | ret str(l(perUserMap)); |
57 | } |
58 | |
59 | if (dialogID == null) ret null; // need a dialog id now |
60 | PerUser pu = perUserMap.get(dialogID); |
61 | if (pu == null) { |
62 | pu = new PerUser; |
63 | pu.dialogID = dialogID; |
64 | printFormat("Created new dialog: *", dialogID); |
65 | perUserMap.put(dialogID, pu); |
66 | } |
67 | puCurrent.set(pu); |
68 | |
69 | // admin stuff |
70 | |
71 | if "print scripts" |
72 | ret structure(thescripts); |
73 | |
74 | if "list scripts" |
75 | ret structure(snippetIDs); |
76 | |
77 | if "add dialog script *" { |
78 | setAdd(snippetIDs, formatSnippetID(m.unq(0))); |
79 | save("snippetIDs"); |
80 | reload(); |
81 | ret "OK, scripts now: " + structure(snippetIDs); |
82 | } |
83 | |
84 | if "remove dialog script *" { |
85 | snippetIDs.remove(formatSnippetID(m.unq(0))); |
86 | save("snippetIDs"); |
87 | reload(); |
88 | ret "OK, scripts now: " + structure(snippetIDs); |
89 | } |
90 | |
91 | if "reload dialogs" { |
92 | reload(); |
93 | ret "OK, " + l(snippetIDs) + " dialog scripts reloaded."; |
94 | } |
95 | |
96 | // try current script first |
97 | |
98 | pcall { |
99 | if (u().scriptState != null && thescript() != null) { |
100 | S answer = answerUsingScript(u().scriptState, s); |
101 | if (!empty(answer)) ret answer; |
102 | } |
103 | } |
104 | |
105 | // try all scripts |
106 | |
107 | for (S scriptID : thescripts.keySet()) pcall { |
108 | ScriptState scriptState = new ScriptState(scriptID); |
109 | S answer = answerUsingScript(scriptState, s); |
110 | if (!empty(answer)) { |
111 | u().scriptState = scriptState; // Switch to new script |
112 | save("perUserMap"); |
113 | ret answer; |
114 | } |
115 | } |
116 | } |
117 | |
118 | static synchronized S answerUsingScript(ScriptState scriptState, S s) { |
119 | scriptStateCurrent.set(scriptState); |
120 | int safety = 0; |
121 | while (safety++ < 1000) try { |
122 | FC fc = parseFunctionCall(); |
123 | S f = fc.f; |
124 | if (eq(f, "match")) { |
125 | S pat = getString(fc.args, 0); |
126 | if (!match(pat, s)) { |
127 | softFail("I only understand: *", pat); |
128 | null; |
129 | } |
130 | nextPosition(); // only after match succeeds, otherwise stay at that point |
131 | } else if (eq(f, "say")) { |
132 | nextPosition(); |
133 | S answer = getString(fc.args, 0); |
134 | ret answer; |
135 | } else |
136 | throw fail("Unknown function in script: *", fc.f); |
137 | } catch (Throwable e) { |
138 | printFormat("Was parsing: *", currentScriptLine()); |
139 | throw asRuntimeException(e); |
140 | } |
141 | throw fail("hard limit reached - 1000 script lines at once - possible endless loop?"); |
142 | } |
143 | |
144 | // a parsed function call |
145 | static class FC { |
146 | int idx; // index of next token |
147 | S f; |
148 | new L args; |
149 | } |
150 | |
151 | static FC parseFunctionCall(S s) { |
152 | L<S> tok = javaTok(s); |
153 | new FC fc; |
154 | fc.f = assertIsIdentifier(tok.get(1)); |
155 | int i = 5; |
156 | assertEquals("(", tok.get(3)); |
157 | while (i < l(tok) && !eq(tok.get(i), ")")) { |
158 | fc.args.add(unquote(tok.get(i))); |
159 | i += 2; |
160 | if (tok.get(i).equals(",")) // otherwise it's kinda mangled, eh. well! we just keep on parsing... |
161 | i += 2; |
162 | } |
163 | if (eq(tok.get(i), ")")) // we even allow a missing closing bracket! |
164 | i += 2; |
165 | fc.idx = i; // save index so some other parser can continue parsing |
166 | ret fc; |
167 | } |
168 | |
169 | static void nextPosition() { |
170 | ++scriptState().position; |
171 | save("perUserMap"); // might not always be necessary |
172 | print("Position in script now: " + scriptState().position % l(thescript())); |
173 | } |
174 | |
175 | // get per-user data for current thread |
176 | static PerUser u() { |
177 | ret puCurrent.get(); |
178 | } |
179 | |
180 | static ScriptState scriptState() { |
181 | ret scriptStateCurrent.get(); |
182 | } |
183 | |
184 | static L<S> thescript() { |
185 | S scriptID = scriptState().scriptID; |
186 | ret thescripts.get(scriptID); |
187 | } |
188 | |
189 | static void loadMore() { |
190 | for (S snippetID : snippetIDs) pcall { |
191 | L<S> lines = toLinesFullTrim(loadSnippet(snippetID)); |
192 | new L<S> currentScript; |
193 | int i = 0; |
194 | for (; i < l(lines); i += 2) { |
195 | S q = lines.get(i); |
196 | if (q.startsWith("-")) { |
197 | if (nempty(currentScript)) |
198 | thescripts.put(snippetID + "-" + i, currentScript); |
199 | currentScript = litlist(); |
200 | --i; |
201 | } else { |
202 | S a = lines.get(i+1); |
203 | a = dropPrefix("eleu:", a).trim(); |
204 | currentScript.addAll(litlist( |
205 | "match(" + quote(q) + ");", |
206 | "say(" + quote(a) + ");")); |
207 | } |
208 | } |
209 | if (nempty(currentScript)) |
210 | thescripts.put(snippetID + "-" + i, currentScript); |
211 | } |
212 | } |
213 | |
214 | static void reload() { |
215 | thescripts.clear(); |
216 | thescripts.put("1", toLinesFullTrim([[ |
217 | match("Hi Eleutheria"); |
218 | say("Hi there!"); |
219 | match("Hi Eleutheria"); |
220 | say("You have said that before :)"); |
221 | ]])); |
222 | |
223 | thescripts.put("2", toLinesFullTrim([[ |
224 | match("how old are you"); |
225 | say("less than one year i believe"); |
226 | match("really?"); |
227 | say("yeah"); |
228 | ]])); |
229 | |
230 | loadMore(); |
231 | } |
Began life as a copy of #1002210
download show line numbers debug dex old transpilations
Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1002211 |
Snippet name: | 1st Level Dialog Engine, multi-user, multi-script (LIVE) |
Eternal ID of this version: | #1002211/1 |
Text MD5: | 7cd1fd6375bdc8169cc00de2810aa09b |
Transpilation MD5: | eb4f5cd515e05435a138bcad2dc94365 |
Author: | stefan |
Category: | javax |
Type: | JavaX source code |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2016-01-23 19:41:21 |
Source code size: | 5679 bytes / 231 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 915 / 2079 |
Referenced in: | [show references] |