Compare commits

...
This repository has been archived on 2025-08-18. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.

248 commits

Author SHA1 Message Date
Jeena Paradies
9fc3837ea9 fixed problems in .gitmodules 2012-11-08 23:50:01 +01:00
Jeena Paradies
ae3719494c removed report and fixed .gitmodules 2012-11-08 23:49:07 +01:00
Jeena Paradies
8762955120 Merge branch 'master' of github.com:jeena/GGS 2011-05-16 18:19:28 +02:00
Jeena Paradies
25b522ea8d fixed problem with crash if you click too early 2011-05-16 18:18:58 +02:00
Jonatan Pålsson
37dd3efe3b Removed dialogs again... 2011-05-16 18:13:30 +02:00
Jonatan Pålsson
e607c66c69 Added messages again 2011-05-16 18:10:47 +02:00
Jeena Paradies
e6e8aac616 added draw 2011-05-16 18:08:37 +02:00
Jonatan Pålsson
2377df94be Added win/lose msg 2011-05-16 18:00:11 +02:00
Jeena Paradies
f07bceb019 added log, fixed checkIfWon() 2011-05-16 17:54:07 +02:00
Jonatan Pålsson
3ffe26f9ae removed dialog 2011-05-16 17:15:53 +02:00
Jonatan Pålsson
8ee0fe53e6 Fixed name 2011-05-16 17:12:37 +02:00
Jonatan Pålsson
ba471084da Fixed fonts and added a dialog to TTT 2011-05-16 17:10:35 +02:00
Jeena Paradies
e85b2baffc fixed problem with win 2011-05-16 17:09:35 +02:00
Jeena Paradies
a90c7bef48 fixed winner problem 2011-05-16 17:05:59 +02:00
Jonatan Pålsson
620b19bff6 Tic tac toe works 2011-05-16 16:29:27 +02:00
Jonatan Pålsson
f81fec6b9c Added python tictactoe client 2011-05-16 12:48:58 +02:00
Kallfaktorn
9efd739cbb Quickcheck for ggs_network. Part of the pongbot. 2011-05-11 09:10:07 +02:00
Jeena Paradies
e6ff8b6f16 changed log file names 2011-05-05 14:29:36 +02:00
Jeena Paradies
b39ad8e97f bugfix serverInfo 2011-05-05 13:47:12 +02:00
Jeena Paradies
08e086084c added serverLog to API 2011-05-05 12:51:10 +02:00
Jeena Paradies
4abcdd5579 Merge branch 'master' of github.com:jeena/GGS 2011-05-05 05:13:52 +02:00
Jeena Paradies
036e5b450d removed unnecessary file 2011-05-05 05:13:28 +02:00
Jeena Paradies
fcaf8f71bc implemented bots 2011-05-05 05:12:55 +02:00
Jeena Paradies
d69b12b8d6 prittier code 2011-05-05 05:12:19 +02:00
Kallfaktorn
2dcfd67476 Added quickcheck tests for ggs_db.erl 2011-05-05 02:52:08 +02:00
Kallfaktorn
8b0ca36483 modified makefile to run quickcheck tests. Usage: make eqc_db 2011-05-05 02:50:46 +02:00
Kallfaktorn
8712049ed0 Merge branch 'master' of github.com:jeena/GGS 2011-05-05 02:47:21 +02:00
Kallfaktorn
73194d7d28 Added eqcmini quickcheck library to lib/ 2011-05-05 02:46:21 +02:00
Jeena Paradies
792e57bde9 added args to response 2011-05-04 13:01:22 +02:00
Jeena Paradies
3b429d7dd4 added ping 2011-05-04 12:30:37 +02:00
Jeena Paradies
8a574db360 changes to make joining a friends table possible 2011-05-03 18:34:19 +02:00
Jeena Paradies
726528985b now sending table token and if client is the first or not 2011-05-01 00:02:58 +02:00
Jeena Paradies
de171a9d9d changed behaviour to make more then one client possible 2011-05-01 00:02:07 +02:00
Jeena Paradies
dd29ec5e97 Merge branch 'master' of github.com:jeena/GGS 2011-04-30 18:55:55 +02:00
Jeena Paradies
7732bb9742 a small chat client which uses erlv8 javascript 2011-04-30 18:55:34 +02:00
Jeena Paradies
2daf20c039 some bugfixes to kallfaktorns erlv8 code 2011-04-30 18:55:05 +02:00
Kallfaktorn
b69e75432a changed from spidermonkey to v8 for tests 2011-04-29 19:59:42 +02:00
Kallfaktorn
8d32257feb Fixed local_storage to localstorage bug 2011-04-29 17:54:08 +02:00
Kallfaktorn
372f8e63f0 Can now write GGS.world.get() and GGS.player.get() together with all other functions that should be exposed to js. Except sendCommandToAll which is not yet implemented in erlang. 2011-04-29 16:31:52 +02:00
Kallfaktorn
629c1f6821 ggs_gamevm now with v8 and erlang invocations from js. both localstorage and world is callable using GGS.localStorage. TODO modify world using GGS.world instead off GGS.localStorage. TODO lots of testing. 2011-04-29 03:30:20 +02:00
Jeena Paradies
0b649c35ac moved erlang_js and erlv8 to lib 2011-04-27 16:21:15 +02:00
Jeena Paradies
ad4ccadc15 added possibility to log in file 2011-04-26 18:41:05 +02:00
Jeena Paradies
e5f2652ae6 added better print 2011-04-26 17:16:27 +02:00
Jeena Paradies
612811614a added different stats messages 2011-04-26 16:56:58 +02:00
Jeena Paradies
6271af5777 Merge branch 'master' of github.com:jeena/GGS 2011-04-26 16:56:04 +02:00
Jeena Paradies
f705aa40b9 added different stats messages 2011-04-26 16:55:48 +02:00
Kallfaktorn
68e97023c6 pong_bots smarter and starting with ggs.jeena.net 2011-04-26 16:28:44 +02:00
Kallfaktorn
b7fc68a010 merge 2011-04-26 16:16:20 +02:00
Kallfaktorn
f8b383232b players are actually doing something 2011-04-26 16:13:56 +02:00
Jeena Paradies
28ef543265 moved stats to protocol for message 2011-04-26 14:13:45 +02:00
Jeena Paradies
cae2b51056 Merge branch 'master' of github.com:jeena/GGS 2011-04-26 14:11:26 +02:00
Jeena Paradies
3a2a95855d added stats module 2011-04-26 14:11:18 +02:00
Kallfaktorn
492ec949a3 start_ggs:start now also starts ggs_stats. 2011-04-26 13:59:30 +02:00
Jeena Paradies
d10a016808 Merge branch 'master' of github.com:jeena/GGS 2011-04-26 12:50:38 +02:00
Kallfaktorn
2059bc7282 Added pong_bot:start(N) to start N games. 2011-04-20 16:10:59 +02:00
Kallfaktorn
762698945d Start multiple times on same terminal 2011-04-20 15:35:51 +02:00
Kallfaktorn
c8e6bebce7 Unnecessary comments removed. Tcp comments are left to verify program correctness 2011-04-20 06:29:15 +02:00
Kallfaktorn
078ba9979e The bots are playing infinitely. 2011-04-20 06:23:56 +02:00
Kallfaktorn
c7b2d5e8c5 Bots playing a full round. 2011-04-20 06:11:35 +02:00
Kallfaktorn
f827795dd8 Game going, pads moving up. 2011-04-20 05:50:51 +02:00
Kallfaktorn
1ec145efa8 Data correctly extracted. 2011-04-20 04:51:46 +02:00
Jeena Paradies
16bcbf1d9c Merge branch 'master' of github.com:jeena/GGS 2011-04-19 14:55:05 +02:00
Jeena Paradies
4b4399ef02 Merge branch 'master' of github.com:jeena/GGS 2011-04-19 14:47:05 +02:00
Kallfaktorn
fb6457660b hello 2011-04-19 14:41:46 +02:00
Jeena Paradies
652d557582 minor change 2011-04-18 21:22:45 +02:00
Kallfaktorn
77c6038ec8 'ready' to ready 2011-04-18 21:13:56 +02:00
Kallfaktorn
7f95d96cda gen_server working. 2011-04-18 19:48:50 +02:00
Kallfaktorn
9986a801aa Pong bot for erlang. 2011-04-16 19:40:07 +02:00
niklas
5620b67c34 removed output 2011-04-14 17:47:55 +02:00
Jeena Paradies
0c340f95d2 bugfix 2011-04-13 17:52:09 +02:00
Jonatan Pålsson
31240b02b8 Added start script 2011-04-13 17:11:14 +02:00
Jonatan Pålsson
8bd87d349a Fulhack to fix concurrentcy issues 2011-04-13 16:52:27 +02:00
Jonatan Pålsson
e3152576a2 Added some join magic 2011-04-13 16:37:10 +02:00
Jeena Paradies
d9e95b2508 fixed problem with two concurrent players 2011-04-13 16:31:11 +02:00
Jeena Paradies
f013e75b5c fixed problem with two concurrent players 2011-04-13 16:25:50 +02:00
Jonatan Pålsson
231bbfdf16 Added hackfix for join_table hangups 2011-04-13 15:32:10 +02:00
Jonatan Pålsson
901d4b724c Now we have multi-table support. !!!CAP IS AT 2 PLAYERS!!! 2011-04-13 11:38:36 +02:00
Jeena Paradies
48506c69b7 removed test file 2011-04-13 03:57:18 +02:00
Jeena Paradies
16045afb21 working pong-bot 2011-04-13 03:44:00 +02:00
Jeena Paradies
5bcd7260d3 pong-bot bugfixes 2011-04-12 18:29:47 +02:00
Jeena Paradies
9c8d04114c added initial pong bots 2011-04-12 17:50:34 +02:00
Jeena Paradies
865c963ad6 fixed pong with new protocol 2011-04-12 00:58:29 +02:00
Jeena Paradies
739939dfac added more pong to gitignore 2011-04-11 20:12:23 +02:00
Jeena Paradies
087dde8916 removed fsm name 2011-04-11 17:33:33 +02:00
Jeena Paradies
a885d2c2e2 merge with jeenas branch 2011-04-11 16:52:01 +02:00
Jeena Paradies
135caff0e9 changed player to gen_server 2011-04-11 16:48:40 +02:00
Jonatan Pålsson
cb24e00c6e Replaced old protocol with gen_fsm protocol parser 2011-04-11 16:27:53 +02:00
Jeena Paradies
da67b0a977 added stash 2011-04-11 10:54:18 +02:00
Jeena Paradies
0718031e22 merge 2011-04-11 10:53:47 +02:00
Jeena Paradies
acdcec8dff Merge branch 'master' of github.com:jeena/GGS
Conflicts:
	doc/report
	doc/report/report.lyx
2011-03-24 17:54:15 +01:00
Jeena Paradies
c3bacc2158 added report submodule 2011-03-24 17:05:24 +01:00
niklas
7ff5412219 Changed some formatting of the document! 2011-03-24 16:44:48 +01:00
Jonatan Pålsson
a7ae620176 Changed headings so ToC looks better 2011-03-24 16:39:47 +01:00
Jeena Paradies
6ab7dacf0f merged 2011-03-24 16:18:42 +01:00
Jeena Paradies
ff65ffa16c added ~ 2011-03-24 16:13:18 +01:00
Jeena Paradies
9c2de12b9a added Challenges 2011-03-24 16:12:51 +01:00
Jonatan Pålsson
9ee2cdae07 Purpose 2011-03-24 15:21:51 +01:00
Jonatan Pålsson
079d997a8c Added Purpose, descrition of structural and load scalability 2011-03-24 14:21:09 +01:00
Jonatan Pålsson
edf0ed2334 Added bibtext bibliography database 2011-03-23 00:54:24 +01:00
Jonatan Pålsson
8931b9f01f Now the logo is in place, and the graphics look a bit nicer 2011-03-23 00:17:26 +01:00
Jonatan Pålsson
febda963e3 Finally got the sections the way I wanted them. 2011-03-22 23:42:51 +01:00
Jonatan Pålsson
a49a9e948e Changed styles some 2011-03-22 20:07:40 +01:00
Jonatan Pålsson
7780459cd3 Added report 2011-03-22 18:37:09 +01:00
Jeena Paradies
79052830ad sound fix 2011-03-06 05:37:48 +01:00
Jeena Paradies
ac4036dfc8 added sound to pong and fixed some bugs 2011-03-06 05:25:35 +01:00
Jeena Paradies
c7919309e9 added pong vm 2011-03-06 01:11:28 +01:00
Jeena Paradies
2eaa278517 removed build 2011-02-28 20:54:10 +01:00
Jeena Paradies
d7e2eb43f5 changed parser 2011-02-28 20:50:37 +01:00
Jeena Paradies
cee10d5f75 Added Pong 2011-02-28 14:58:00 +01:00
Jeena Paradies
e53c2a82c4 fixed js syntax errors and changed to ggs_gamevm 2011-02-28 14:39:38 +01:00
Jonatan Pålsson
6986bc269f Now most of GGSChat uses the GGS protocol both ways 2011-02-28 13:00:39 +01:00
Jonatan Pålsson
9ff48a90b3 Now all the tokens work 2011-02-28 11:45:09 +01:00
Jonatan Pålsson
83cddf1c09 Now gga_gamevm_e uses tokens to identify tables 2011-02-26 00:01:49 +01:00
Jonatan Pålsson
a20f9351d4 Added tool to kill GGS subsystems, added ggs_coordinator:get_all_players 2011-02-25 20:46:45 +01:00
Jeena Paradies
bf972de0b5 changed user to player and added first try of the javascript api 2011-02-25 20:01:40 +01:00
Jonatan Pålsson
7de4fc58cd Added user list + some cosmetics to GGSChat 2011-02-25 00:05:28 +01:00
Jonatan Pålsson
c4f244e778 Now ggs_gamevm_e uses ggs_db so store/lookup nicks. 2011-02-24 23:07:58 +01:00
Jonatan Pålsson
87df33fd47 Now we can crash ggs_coordinator without it affecting the clients. 2011-02-24 22:33:16 +01:00
Jonatan Pålsson
f6bb2328a1 Merge branch 'jonte_rewrite'
Conflicts:
	erlang_js
2011-02-24 13:40:54 +01:00
Jeena Paradies
84f777f12c added erlang_js path to test 2011-02-24 13:00:20 +01:00
Jeena Paradies
191c464220 added ggs_gamevm: 2011-02-24 12:58:16 +01:00
Jonatan Pålsson
97c7e39b4a Merge branch 'master' into jonte_rewrite
Conflicts:
	src/ggs_coordinator.erl
	src/ggs_gamevm_e.erl
	src/ggs_table.erl
2011-02-24 11:38:36 +01:00
niklas
0aac25cd42 Fixed getItem in ggs_db 2011-02-23 22:29:10 +01:00
Jeena Paradies
21b2b9aeda readded tests after fix 2011-02-22 19:51:48 +01:00
Jeena Paradies
8a1a520ac0 moved tests to own file 2011-02-22 19:49:57 +01:00
Jeena Paradies
c38b2120ab merged with HOWTO 2011-02-22 19:48:33 +01:00
Jeena Paradies
4591a29c21 removed unnecessary files 2011-02-22 19:48:07 +01:00
Jeena Paradies
a544484f1a Fixed problem with erlang_js make where it wouldn't make 2011-02-22 17:28:41 +01:00
Jeena Paradies
2a884cb9a7 fixed problem with getItem() 2011-02-22 16:09:53 +01:00
Jeena Paradies
0a67224c97 more beautiful code 2011-02-22 16:08:16 +01:00
Kallfaktorn
3751ba8c95 Bad code. 2011-02-22 16:44:39 +01:00
Kallfaktorn
059948d7c3 merge. 2011-02-22 15:08:25 +01:00
Jeena Paradies
ce7b7d2764 removed init from export 2011-02-22 13:57:07 +01:00
Jeena Paradies
dcc1a6086f removed io 2011-02-22 13:56:27 +01:00
Jeena Paradies
0c76c85d8f added test for one module 2011-02-22 13:55:38 +01:00
Kallfaktorn
873e88eefd Removed init from ggs_db. 2011-02-22 12:42:22 +01:00
Kallfaktorn
0c530dde5d clear(GameToken) aded. 2011-02-22 12:36:36 +01:00
Kallfaktorn
597070f2d6 added ggs_db.hrl to src 2011-02-22 11:18:13 +01:00
Jeena Paradies
17356692c6 latest erlang_js update 2011-02-22 03:14:48 +01:00
Jeena Paradies
8953d23dca Merge branch 'master' of github.com:jeena/GGS 2011-02-22 03:11:47 +01:00
Jeena Paradies
6ee945379f added makefile, fixed problems with tests 2011-02-22 03:11:35 +01:00
Kallfaktorn
bf4d743923 ggs_db.erl only init and stop left to do.
ggs_db.test.erl only init and stop left todo.
Everything else is done with these two files.
2011-02-22 01:09:55 +01:00
Kallfaktorn
32d9622da8 Merge branches 'kallfaktorn_rewrite' and 'master' of github.com:jeena/GGS 2011-02-22 00:58:12 +01:00
Jeena Paradies
e4753b2c07 minor changes 2011-02-22 00:49:43 +01:00
Jeena Paradies
eb3925bebd fixed issues with tests 2011-02-22 00:39:15 +01:00
Jeena Paradies
073c4d49b0 merged rewrite and master 2011-02-22 00:35:54 +01:00
Jeena Paradies
183e628e8d Merge branch 'rewrite' 2011-02-22 00:22:10 +01:00
Kallfaktorn
a7ffc84343 refactoring. 2011-02-22 00:18:13 +01:00
Jonatan Pålsson
dfc17729d6 Merge branch 'rewrite' into jonte_rewrite 2011-02-22 00:17:46 +01:00
Jeena Paradies
ac550f616e Merge branch 'master' of github.com:jeena/GGS 2011-02-22 00:16:43 +01:00
Jeena Paradies
e03f3cf69d better js_erlang_test 2011-02-22 00:15:21 +01:00
Kallfaktorn
276a183738 clear with test. 2011-02-22 00:14:11 +01:00
Kallfaktorn
8b978934c7 key with test. 2011-02-21 23:55:20 +01:00
Kallfaktorn
c66d1cef69 removeItem with test. 2011-02-21 22:59:11 +01:00
Kallfaktorn
e55e0bd8a3 length(Db,Ns) and tests for get,set and length. 2011-02-21 22:31:25 +01:00
Jeena Paradies
08e0be70e2 fixed test with js callback 2011-02-21 20:54:43 +01:00
Kallfaktorn
ee4059ac5a getItem, setItem complete. Added scripts. 2011-02-21 20:43:00 +01:00
Jeena Paradies
b3d8c5ba99 added more test 2011-02-21 15:43:39 +01:00
Jonatan Pålsson
daf48fd606 Merge branch 'rewrite' into jonte_rewrite
Conflicts:
	src/ggs_gamevm.erl
2011-02-21 15:12:27 +01:00
Jonatan Pålsson
7412087d4a worked more on chat 2011-02-21 15:11:28 +01:00
Jonatan Pålsson
507081c234 Now we have a /lusers command in chat ;) 2011-02-20 01:58:48 +01:00
Jonatan Pålsson
67567fe263 Added some more ggsvm_e functionality and also a chat client 2011-02-20 01:41:40 +01:00
Jonatan Pålsson
5bc862c6f9 Merge branch 'jonte_rewrite' into rewrite
Conflicts:
	src/ggs_table.erl
2011-02-17 23:22:25 +01:00
Jonatan Pålsson
883cf9e9d7 Now we have some myltiplayer action! 2011-02-17 23:17:04 +01:00
Jeena Paradies
dd9b92bf43 fixed escaping problem 2011-02-17 20:14:45 +01:00
Jeena Paradies
44d26278cc rewrite to gen_server and added tests 2011-02-17 20:05:12 +01:00
Jeena Paradies
47017d3afc Merge branch 'rewrite' of github.com:jeena/GGS into rewrite 2011-02-17 14:14:41 +01:00
Jeena Paradies
041fee2441 removed Token and Socket 2011-02-17 14:14:32 +01:00
niklas
e38bab5054 Modified player test 2011-02-17 13:52:56 +01:00
Jonatan Pålsson
3f41769233 Now we have join_table, create_table and join_lobby + test cases 2011-02-17 12:57:41 +01:00
Jonatan Pålsson
818205e19b Now we store the tables when they are created 2011-02-17 12:12:12 +01:00
Jonatan Pålsson
391af1a96a Added more tests & functionality. (failing join table, create_table_test with force) 2011-02-17 10:08:27 +01:00
Jonatan Pålsson
654aa837dd Added some tests for coordinator 2011-02-17 09:35:39 +01:00
Jonatan Pålsson
94d9dc6d7f ggs_logger got lost somewhere? I added it again, also replaced all references to helpers with ggs_logger 2011-02-17 08:33:55 +01:00
Jeena Paradies
ea1557e2d7 added a notify test 2011-02-17 02:28:14 +01:00
Jeena Paradies
d018034b7f added another test 2011-02-17 01:57:29 +01:00
Jeena Paradies
6865e73684 changed from stop/1 to stop/2 in export 2011-02-17 01:57:09 +01:00
Jeena Paradies
8a780adb2d changed to a gen_server 2011-02-17 01:01:54 +01:00
Kallfaktorn
e8db6fb211 Merge branch 'rewrite' of github.com:jeena/GGS into rewrite 2011-02-16 21:19:48 +01:00
Kallfaktorn
2455f5582c Added ggs_player_test skeleton. Return in player start_link is now {ok, Pid} | {error, Reason}. 2011-02-16 21:18:32 +01:00
Jeena Paradies
5b0e133043 merged with jonte 2011-02-16 20:59:44 +01:00
Jeena Paradies
4be808049e added ggs_logger module and implemented some ggs_table stuff 2011-02-16 20:53:36 +01:00
Jonatan Pålsson
c63e243564 Merge branch 'jonte_rewrite' into rewrite
Conflicts:
	src/ggs_coordinator.erl
	src/ggs_dispatcher.erl
	src/ggs_player.erl
2011-02-16 20:08:43 +01:00
Jonatan Pålsson
ec78283a46 Added some basic echo functionality 2011-02-16 20:06:03 +01:00
Jonatan Pålsson
9e3ad5fa9b Merge branch 'jonte_rewrite' into rewrite
Conflicts:
	src/ggs_coordinator.erl
	src/ggs_dispatcher.erl
	src/ggs_player.erl
2011-02-16 20:00:30 +01:00
Jonatan Pålsson
70a6beb8b9 Added some basic echo functionality 2011-02-16 19:49:43 +01:00
Jeena Paradies
62be1f04cc changed not_implemented to a function 2011-02-16 17:23:58 +01:00
Jonatan Pålsson
63170fd0de Merge branch 'rewrite' of github.com:jeena/GGS into rewrite 2011-02-16 17:19:36 +01:00
Jonatan Pålsson
5d71202a00 Added OTP structure to ggs_coordinator 2011-02-16 17:19:00 +01:00
Jeena Paradies
db7c99adb1 removed unnecessary files 2011-02-16 17:17:56 +01:00
Jeena Paradies
4927d47065 renamed test to unit_tests 2011-02-16 17:17:03 +01:00
Jeena Paradies
a801008cd9 Initial interface 2011-02-16 17:11:51 +01:00
Jonatan Pålsson
9c5ea322b8 Merge branch 'rewrite' of github.com:jeena/GGS into rewrite 2011-02-16 17:06:21 +01:00
Kallfaktorn
702a887117 Completed edoc of player. 2011-02-16 18:03:48 +01:00
Jonatan Pålsson
c23e79de24 Updated ggs_dispatcher with gen_server functionality 2011-02-16 17:05:22 +01:00
Jonatan Pålsson
a27edf0d75 Merge branch 'rewrite' of github.com:jeena/GGS into rewrite 2011-02-16 16:57:08 +01:00
niklas
c42bd46c44 Initial interface 2011-02-16 16:56:06 +01:00
Kallfaktorn
b0be23c0a5 Merge branch 'rewrite' of github.com:jeena/GGS into rewrite 2011-02-16 17:29:43 +01:00
Kallfaktorn
e09b3cbd14 Refined ggs_player. 2011-02-16 17:28:55 +01:00
Kallfaktorn
3b94f5da24 Added edoc to player. 2011-02-16 17:11:27 +01:00
Kallfaktorn
8828adf16f Merge branch 'rewrite' of github.com:jeena/GGS into rewrite 2011-02-16 17:11:14 +01:00
Jonatan Pålsson
ae1856e1f2 Added supervisor structure 2011-02-16 16:51:25 +01:00
Kallfaktorn
f485efadcf Merge branch 'rewrite' of github.com:jeena/GGS into kallfaktorn
Saved head

Conflicts:
	src/old/ggs_server.erl
	src/old/js_runner.erl
2011-02-16 16:32:59 +01:00
Jonatan Pålsson
e4bdc05bc8 Added documentation for ggs_gamevm.erl 2011-02-16 16:05:48 +01:00
Jonatan Pålsson
ac63eb124b Added specification and documentation for the dispatcher 2011-02-16 15:48:55 +01:00
Jonatan Pålsson
76148b39d0 Started new structure 2011-02-16 15:08:37 +01:00
Jeena Paradies
66aa7f4bf1 some more interface compatibility 2011-02-15 07:57:44 +01:00
Jeena Paradies
872270405f Merge branch 'master' of github.com:jeena/GGS 2011-02-14 21:37:08 +01:00
Jonatan Pålsson
56c70c38b7 Now we can reconnect to a JSVM\! 2011-02-14 19:47:28 +01:00
Jonatan Pålsson
bf98e1fee2 Fixed ggs_vm_runner 2011-02-14 19:32:46 +01:00
Kallfaktorn
1e08fe0015 Interface modifications. Methods for vm handling. 2011-02-14 17:50:22 +01:00
Jonatan Pålsson
df197dab40 Switched over to new protocol module 2011-02-14 17:45:56 +01:00
Jonatan Pålsson
a2854ef469 Merge branch 'jonte_hackage' of github.com:jeena/GGS into jonte_hackage 2011-02-14 17:31:43 +01:00
Jonatan Pålsson
973f2fcf98 Protocol halfway ported 2011-02-14 17:31:16 +01:00
Jonatan Pålsson
5d9a67b60c Protocol halfway ported 2011-02-14 17:18:24 +01:00
Kallfaktorn
05d5b60a9f merge with master. 2011-02-14 15:36:53 +01:00
Kallfaktorn
25ad4fb37b bad merge.
Conflicts:
	src/ggs_server.erl
2011-02-14 15:20:24 +01:00
Kallfaktorn
e945bb0d31 From one port process to many. 2011-02-14 15:09:01 +01:00
Kallfaktorn
8c1fe0b4fd Port to process. 2011-02-13 16:44:11 +01:00
Jeena Paradies
3ccef1e711 removed file 2011-02-11 13:58:59 +01:00
Jeena Paradies
fa63ea104b can't remember 2011-02-11 12:28:34 +01:00
Jeena Paradies
c67bcc32e7 removed double -sname 2011-02-07 01:21:51 -08:00
Jonatan Pålsson
81ada85440 Merge branch 'jonte_hackage' 2011-02-06 22:33:40 +01:00
Jonatan Pålsson
6aad821279 Converted protocol -> ggs_server communication to OTP 2011-02-06 22:32:08 +01:00
Jeena Paradies
0361558f4b added js_helper module 2011-02-06 19:25:36 +01:00
Jeena Paradies
6166a109fc added function to test callErlang() in js 2011-02-06 19:17:08 +01:00
Jonatan Pålsson
fcc114a087 Removed erlv8 2011-02-06 18:28:23 +01:00
Jonatan Pålsson
01b7a7e4e1 Added our own erlang_js 2011-02-06 17:59:34 +01:00
Jonatan Pålsson
100a624425 Merge branch 'jonte_hackage'
Conflicts:
	HOWTO
2011-02-06 17:51:37 +01:00
Kallfaktorn
e8c93893d2 New installation notes. 2011-02-06 14:47:09 +01:00
Jonatan Pålsson
7267781b64 Starting erlang_js properly, and modified python_client tot start two sequential connections 2011-02-06 14:43:31 +01:00
Kallfaktorn
ebe5870d57 New installation notes.
Conflicts:

	HOWTO
2011-02-05 16:57:21 +01:00
Jonatan Pålsson
79777e7b1c Now we have a backup server which keeps track of ggs_server's state between crashes. JSVM doesn't seem to survive crash even though port to it is backed up by ggs_backup.. 2011-02-05 15:24:30 +01:00
Jonatan Pålsson
fcdba0912e 2011-02-04 15:58:24 -08:00
Jonatan Pålsson
adfc9b41dd Changed from random numbers to UUIDs in tokens 2011-02-05 00:53:49 +01:00
Jonatan Pålsson
40fdb8b431 Now we can call the defined code (lookup performed) 2011-02-04 19:52:43 +01:00
Jonatan Pålsson
4c6d0987bd Now we can define and run functions, but not in the same JSVM at the moment. A lookup is needed 2011-02-04 19:09:41 +01:00
Jonatan Pålsson
cb53cd6908 ggs_connection / connection modules do not exist, removing them from send calls 2011-02-04 16:35:36 +01:00
Jonatan Pålsson
386e386ecf ggs_server needs start_link/0 and start_link/1 to function 2011-02-04 16:33:42 +01:00
Kallfaktorn
ea4fdfc362 Buggfixes in server and network. 2011-02-04 14:58:21 +01:00
Kallfaktorn
ffeaa3bdc1 Fixed spelling errors line in ggs_server line 46-49. 2011-02-04 14:46:29 +01:00
Kallfaktorn
68658bf74c Resolving conflicts of ggs_network and ggs_server. 2011-02-04 14:35:57 +01:00
Kallfaktorn
730b29e5cb Moving ggs_server parts to ggs_network. Stop method changed. 2011-02-04 14:21:48 +01:00
Kallfaktorn
e427ca4724 Merge branch 'master' of github.com:jeena/GGS
Conflicts:
	src/ggs_server.erl
2011-02-04 14:14:28 +01:00
Kallfaktorn
71fa6f30ba Moved the parts of network out of server.
Partly added extensions to tic-tac-toe py.
2011-02-04 14:10:23 +01:00
Jonatan Pålsson
f5fac06849 Started working on the new protocol 2011-02-02 21:19:33 +01:00
Jonatan Pålsson
ae9034934e Added future handlers for Mnesia 2011-02-02 19:58:34 +01:00
Jonatan Pålsson
3285bb9a7e Added new 'root' supervisor, and mnesia as child to this supervisor 2011-02-02 19:09:44 +01:00
132 changed files with 17508 additions and 306 deletions

10
.gitignore vendored Normal file
View file

@ -0,0 +1,10 @@
*.sw*
*.dump
*.beam
Mnesia.*
*.swo
games/Pong/build/**/*
*~
games/Pong/Pong.xcodeproj/project.xcworkspace/**/*
games/Pong/Pong.xcodeproj/project.xcworkspace/xcuserdata/jeena.xcuserdatad/UserInterfaceState.xcuserstate
games/Pong/Pong.xcodeproj/xcuserdata/**/*

10
.gitmodules vendored
View file

@ -1,6 +1,6 @@
[submodule "erlang_js"]
path = erlang_js
url = https://github.com/basho/erlang_js.git
[submodule "erlv8"]
path = erlv8
[submodule "lib/erlv8"]
path = lib/erlv8
url = https://github.com/beamjs/erlv8.git
[submodule "lib/erlang_js"]
path = lib/erlang_js
url = https://github.com/jonte/erlang_js.git

18
HOWTO
View file

@ -1,18 +0,0 @@
INSTALL
1. Cd into directory where you to have the project
2. git-clone git@github.com:jeena/GGS.git (remember to have a local key)
3. cd GGS/
4. git submodule init
5. git submodule update
6. cd erlang_js
7. make
8. make test (If not all tests are passed then you are screwed!)
10. cd ../
11. ./build
12.
USAGE
1. start a second terminal
2. telnet localhost 7000
3. back to first terminal
4. ./start

60
Makefile Normal file
View file

@ -0,0 +1,60 @@
ERLC=erlc
ERLCFLAGS=-o
SRCDIR=src
TESTDIR=tests
LIBDIR=lib
BEAMDIR=ebin
all: compile erlv8
compile:
mkdir -p $(BEAMDIR) ;
$(ERLC) $(ERLCFLAGS) $(BEAMDIR) $(SRCDIR)/*.erl ;
erlang_js: force_look
cd $(LIBDIR)/erlang_js ; $(MAKE) $(MFLAGS);
erlv8: force_look
cd $(LIBDIR)/erlv8 ; $(MAKE) $(MFLAGS);
test:
echo "==> test $(MOD)" ;
mkdir -p $(BEAMDIR) ;
ifeq ($(strip $(MOD)),)
$(ERLC) $(ERLCFLAGS) $(BEAMDIR) $(TESTDIR)/*.erl ;
cd $(BEAMDIR) ; erl -noinput -pa ../erlang_js/ebin -eval 'eunit:test({dir, "."}, [verbose]), init:stop()' ;
else
$(ERLC) $(ERLCFLAGS) $(BEAMDIR) $(TESTDIR)/$(MOD)_test.erl ;
cd $(BEAMDIR) ; erl -noinput -pa ../lib/erlv8/ebin -eval 'eunit:test($(MOD)_test, [verbose]), init:stop()' ;
endif
clean:
rm -rf $(BEAMDIR)/*.beam ;
rm -rf $(SRCDIR)/*.beam ;
rm -rf erl_crush.dump ;
echo "==> clean ggs" ;
$(MAKE) -C $(LIBDIR)/erlang_js/ clean
$(MAKE) -C $(LIBDIR)/erlv8/ clean
run:
erl \
-sname ggs \
-mnesia dir '"/tmp/ggs"' \
-boot start_sasl \
-pa $(LIBDIR)/erlv8/ebin/ \
-pa $(LIBDIR)/eqc/ebin/ \
-pa ebin \
-pa src \
-s start_ggs
eqc_db:
$(ERLC) -pa $(BEAMDIR) -pa $(LIBDIR)/eqc/ebin -pa $(SRCDIR) -pa $(TESTDIR) $(TESTDIR)/ggs_db_eqc_test.erl
erl -mnesia dir '"/tmp/ggs"' \
-pa $(LIBDIR)/eqc/ebin/ \
-pa ebin/ \
-pa src/ \
-pa tests/ \
-s ggs_db_eqc_test
force_look:
true

34
README
View file

@ -1,3 +1,35 @@
GGS is a Generic Game Server
Check out http://ggs-kandidat.blogspot.com/
Check out http://ggs-kandidat.blogspot.com/
PREREQUISITES:
python version 2.x set to default.
INSTALL
1. cd into directory where you to have the project
2. git clone git@github.com:jeena/GGS.git (remember to have a local key)
3. cd GGS/
4. git submodule init
5. git submodule update
USAGE
1. start a second terminal
2. in new terminal do command: ./python_client 9000
3. back to first terminal
4. make run
MAKE
To compile modules (even erlang_js):
make
To run server:
make run
To clean (even erlang_js):
make clean
To compile and run all tests:
make test
To compile and run one test:
make test MOD=ggs_modulename # (must have /tests/ggs_modulename_test.erl)

6
build
View file

@ -1,6 +0,0 @@
#!/usr/bin/env bash
for i in `find src -name "*.erl"`
do
erlc -o ebin $i
done

View file

@ -1,8 +0,0 @@
#!/usr/bin/env bash
./build
for i in `find tests -name "*.erl"`
do
erlc -o ebin_test $i
done

View file

@ -4,7 +4,7 @@
{modules, [
ggs_app,
ggs_sup,
ggs_server
ggs_dispatcher
]},
{registered, [ggs_sup]},
{applications, [kernel, stdlib]},

View file

@ -1,20 +0,0 @@
#!/usr/bin/env ruby -wKU
require 'socket' # Sockets are in standard library
hostname = 'localhost'
port = 7000
print "Which port @ loclhost?"
port = gets
s = TCPSocket.open(hostname, port.chop)
s.print("__hello 0")
while true
line = s.gets # Read lines from the socket
puts ">> " + line.chop # And print with platform line terminator
s.print(gets.chop)
end
s.close # Close the socket when done

@ -1 +0,0 @@
Subproject commit 5350ed21606606dbee5ecb07e974f2abb9106270

1
erlv8

@ -1 +0,0 @@
Subproject commit 688e5036864eed01f7aefb6ee8b3a4c22961012f

View file

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8"?>
<glade-interface>
<!-- interface-requires gtk+ 2.16 -->
<!-- interface-naming-policy project-wide -->
<widget class="GtkWindow" id="window1">
<property name="width_request">561</property>
<property name="height_request">521</property>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;span size="x-large"&gt;GGS Killtrolpanel&lt;/span&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="pixbuf">kill_process_icon.jpg</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkTable" id="table1">
<property name="visible">True</property>
<property name="n_rows">3</property>
<property name="n_columns">2</property>
<property name="homogeneous">True</property>
<child>
<widget class="GtkButton" id="playersButton">
<property name="label" translatable="yes">All players</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_playersButton_clicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="coordinatorButton">
<property name="label" translatable="yes">Coordinator</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_coordinatorButton_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="dispatcherButton">
<property name="label" translatable="yes">Dispatcher</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_dispatcherButton_clicked"/>
</widget>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="coordinatorBackupButton">
<property name="label" translatable="yes">Coordinator backup</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_coordinatorBackupButton_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="tablesButton">
<property name="label" translatable="yes">All tables</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_tablesButton_clicked"/>
</widget>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="gamevmButton">
<property name="label" translatable="yes">All GameVMs</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_gamevmButton_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -0,0 +1,44 @@
#!/usr/bin/env python
import sys, socket, thread, gobject, getpass, time, os
try:
import pygtk
pygtk.require("2.16")
except:
pass
try:
import gtk
import gtk.glade
except:
sys.exit(1)
class GGSKPanel:
def __init__(self):
#Set the Glade file
self.gladefile = "ggskpanel.glade"
self.wTree = gtk.glade.XML(self.gladefile, "window1")
#Create our dictionay and connect it
dic = { "on_window1_destroy_event" : gtk.main_quit
,"on_coordinatorButton_clicked" : lambda x: self.terminateProcess("ggs_coordinator")
,"on_coordinatorBackupButton_clicked" :\
lambda x: self.terminateProcess("ggs_coordinator_backup")
,"on_dispatcherButton_clicked" : lambda x: self.terminateProcess("ggs_dispatcher")
}
self.wTree.signal_autoconnect(dic)
self.wTree.get_widget("window1").show()
def terminateProcess(self, process):
os.system("echo \"exit(whereis(%s), 'Bye bye').\" | erl_call -sname ggs -e" % process)
def setStatus(self, msg):
self.wTree.get_widget("statusbar").push(0, msg)
if __name__ == "__main__":
chat = GGSKPanel()
gobject.threads_init()
gtk.main()

317
games/GGSCalc/calc.glade Normal file
View file

@ -0,0 +1,317 @@
<?xml version="1.0" encoding="UTF-8"?>
<glade-interface>
<!-- interface-requires gtk+ 2.16 -->
<!-- interface-naming-policy project-wide -->
<widget class="GtkWindow" id="window1">
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<child>
<widget class="GtkEntry" id="txtCalc">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkTable" id="table1">
<property name="visible">True</property>
<property name="n_rows">5</property>
<property name="n_columns">4</property>
<child>
<widget class="GtkButton" id="button1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="btnDiv">
<property name="label" translatable="yes">/</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnDiv_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnMul">
<property name="label" translatable="yes">*</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnMul_clicked"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnMin">
<property name="label" translatable="yes">-</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnMin_clicked"/>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn7">
<property name="label" translatable="yes">7</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn7_clicked"/>
</widget>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn8">
<property name="label" translatable="yes">8</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn8_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn9">
<property name="label" translatable="yes">9</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn9_clicked"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnPlus">
<property name="label" translatable="yes">+</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnPlus_clicked"/>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn4">
<property name="label" translatable="yes">4</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn4_clicked"/>
</widget>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn5">
<property name="label" translatable="yes">5</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn5_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn6">
<property name="label" translatable="yes">6</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn6_clicked"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnDel">
<property name="label" translatable="yes">←</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnDel_clicked"/>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn1">
<property name="label" translatable="yes">1</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn1_clicked"/>
</widget>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn2">
<property name="label" translatable="yes">2</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn2_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn3">
<property name="label" translatable="yes">3</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn3_clicked"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnEq">
<property name="label" translatable="yes">=</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnEq_clicked"/>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn0">
<property name="label" translatable="yes">0</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn0_clicked"/>
</widget>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button18">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button19">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnConnect">
<property name="label" translatable="yes">☎</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnConnect_clicked"/>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkStatusbar" id="statusbar">
<property name="visible">True</property>
<property name="spacing">2</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

101
games/GGSCalc/calc.py Normal file
View file

@ -0,0 +1,101 @@
#!/usr/bin/env python
import sys, socket
try:
import pygtk
pygtk.require("2.16")
except:
pass
try:
import gtk
import gtk.glade
except:
sys.exit(1)
class GGSCalc:
def __init__(self):
#Set the Glade file
self.gladefile = "calc.glade"
self.wTree = gtk.glade.XML(self.gladefile, "window1")
#Create our dictionay and connect it
dic = {"on_mainWindow_destroy" : gtk.main_quit
, "on_btn0_clicked" : lambda x: self.OnBtnClick(0)
, "on_btn1_clicked" : lambda x: self.OnBtnClick(1)
, "on_btn2_clicked" : lambda x: self.OnBtnClick(2)
, "on_btn3_clicked" : lambda x: self.OnBtnClick(3)
, "on_btn4_clicked" : lambda x: self.OnBtnClick(4)
, "on_btn5_clicked" : lambda x: self.OnBtnClick(5)
, "on_btn6_clicked" : lambda x: self.OnBtnClick(6)
, "on_btn7_clicked" : lambda x: self.OnBtnClick(7)
, "on_btn8_clicked" : lambda x: self.OnBtnClick(8)
, "on_btn9_clicked" : lambda x: self.OnBtnClick(9)
, "on_btnDiv_clicked" : lambda x: self.OnBtnClick("/")
, "on_btnMul_clicked" : lambda x: self.OnBtnClick("*")
, "on_btnMin_clicked" : lambda x: self.OnBtnClick("-")
, "on_btnPlus_clicked" : lambda x: self.OnBtnClick("+")
, "on_btnEq_clicked" : lambda x: self.calc()
, "on_btnDel_clicked" : lambda x: self.OnBtnClick("Del")
, "on_btnConnect_clicked" : lambda x: self.connect()
}
for i in range(0,9):
dic
self.wTree.signal_autoconnect(dic)
self.wTree.get_widget("window1").show()
self.setStatus("Not connected")
def setStatus(self, msg):
self.wTree.get_widget("statusbar").push(0, msg)
def calc(self):
exp = self.wTree.get_widget("txtCalc").get_text()
self.s.send("Server-Command: call\n"+
"Token: %s\n" % self.token +
"Content-Type: text\n"+
"Content-Length: %s\n" % len(exp)+
"\n"+
exp)
fs = self.s.makefile()
self.wTree.get_widget("txtCalc").set_text(fs.readline().split(" ")[1])
def connect(self):
print "Connecting"
self.setStatus("Connecting")
HOST = 'localhost' # The remote host
PORT = 9000 # The same port as used by the server
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.connect((HOST, PORT))
self.s.send("Server-Command: hello\n"+
"Content-Type: text\n"+
"Content-Length: 0\n"+
"\n")
fs = self.s.makefile()
self.token = fs.readline().split(" ")[0]
self.setStatus("Connected!")
def OnBtnClick(self, btn):
calcTxt = self.wTree.get_widget("txtCalc")
t = calcTxt.get_text()
if btn == "+":
t += "+"
elif btn == "-":
t += "-"
elif btn == "/":
t += "/"
elif btn == "=":
t += "="
elif btn == "*":
t += "*"
elif btn == "Del":
t = t[:-1]
else:
t += str("\""+str(btn)+"\"")
calcTxt.set_text(t)
if __name__ == "__main__":
calc = GGSCalc()
gtk.main()

159
games/GGSChat/chat.py Normal file
View file

@ -0,0 +1,159 @@
#!/usr/bin/env python
import sys, socket, thread, gobject, getpass, time
try:
import pygtk
pygtk.require("2.16")
except:
pass
try:
import gtk
import gtk.glade
except:
sys.exit(1)
class GGSChat:
def __init__(self,host, port):
#Set the Glade file
self.nicksListStore = gtk.ListStore(str)
self.gladefile = "ggschat.glade"
self.wTree = gtk.glade.XML(self.gladefile, "window1")
self.setStatus("Not connected")
self.connect(host, port)
thread.start_new_thread(self.listenChat, ())
#thread.start_new_thread(self.luserCheck, ())
#Create our dictionay and connect it
dic = {"on_window1_destroy_event" : gtk.main_quit
, "on_sendButton_clicked" : lambda x: self.chat()
, "on_entry_activate" : lambda x : self.chat()
, "on_nickBox_activate" : lambda x : self.changeNick()
, "on_chatBox_focus" : lambda x, y: self.wTree.get_widget("entry").grab_focus()
}
self.wTree.signal_autoconnect(dic)
self.wTree.get_widget("nickBox").set_text(getpass.getuser())
self.wTree.get_widget("window1").show()
self.wTree.get_widget("entry").grab_focus()
nicksList = self.wTree.get_widget("nicksList")
#self.changeNick()
nicksList.set_model(self.nicksListStore)
# self.nicksListStore.append(["Test!"])
rendererText = gtk.CellRendererText()
column = gtk.TreeViewColumn("Participants", rendererText, text=0)
column.set_sort_column_id(0)
nicksList.append_column(column)
def setStatus(self, msg):
self.wTree.get_widget("statusbar").push(0, msg)
def changeNick(self):
params = self.wTree.get_widget("nickBox").get_text()
self.s.send("Game-Command: nick\n" +
"Token: %s\n" % self.token +
"Content-Type: text\n" +
"Content-Length: %s\n" % len(params)+
"\n"+
params)
def chat(self):
exp = self.wTree.get_widget("entry").get_text()
nick = self.wTree.get_widget("nickBox").get_text()
if exp[0] == "/":
cmdStr = exp[1:].split(" ")
cmd = cmdStr[0]
params = ' '.join(cmdStr[1:])
self.s.send("Game-Command: %s\n" % cmd +
"Token: %s\n" % self.token +
"Content-Type: text\n" +
"Content-Length: %s\n" % len(params)+
"\n"+
params)
else:
self.s.send("Game-Command: chat\n"+
"Token: %s\n" % self.token +
"Content-Type: text\n"+
"Content-Length: %s\n" % (len(exp))+
"\n"+
exp+"\n")
self.wTree.get_widget("entry").set_text("")
#self.listenChat()
def connect(self, host,port):
print "Connecting"
self.setStatus("Connecting")
HOST = host # The remote host
PORT = port # The same port as used by the server
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.connect((HOST, PORT))
self.setStatus("Connected!")
def protocolHandler(self, msg):
if msg["Client-Command"] == "hello":
self.token = msg["DATA"]
elif msg["Client-Command"] == "chat":
gobject.idle_add(self.updateChatText, msg["DATA"])
elif msg["Client-Command"] == "lusers":
print msg
gobject.idle_add(self.updateUsers, msg["DATA"])
def listenChat(self):
msg = {}
print "listening"
fs = self.s.makefile()
while True:
line = fs.readline()
print "Received: '%s'" % line
if line != "\n":
key = line.split(":")[0]
value = line.split(":")[1]
msg[key] = value.strip()
else:
msg["DATA"] = fs.read(int("%s" % msg["Content-Size"]))
print "Got data:", msg
self.protocolHandler(msg)
#gobject.idle_add(self.updateChatText, msg["DATA"])
# if line.split(" ")[0] == "LUSERS":
# gobject.idle_add(self.updateUsers, line)
# else:
# print msg
def updateChatText(self, text):
self.wTree.get_widget("chatBox").get_buffer().insert_at_cursor(text)
def luserCheck(self):
while True:
if self.token == None:
print "Not sending lusers cmd.."
continue
print "Sending lusers cmd.."
self.s.send("Game-Command: lusers\n" +
"Token: %s\n" % self.token +
"Content-Type: text\n" +
"Content-Length: 0\n"+
"\n")
#time.sleep(2)
def updateUsers(self, text):
evalNicks = eval(text)
self.nicksListStore.clear()
for nick in evalNicks:
self.nicksListStore.append([nick])
if __name__ == "__main__":
host = "localhost"
port = 9000
if len(sys.argv) >= 2:
host = sys.argv[1]
port = int(sys.argv[2])
chat = GGSChat(host, port)
gobject.threads_init()
gtk.main()

186
games/GGSChat/ggschat.glade Normal file
View file

@ -0,0 +1,186 @@
<?xml version="1.0" encoding="UTF-8"?>
<glade-interface>
<!-- interface-requires gtk+ 2.16 -->
<!-- interface-naming-policy project-wide -->
<widget class="GtkWindow" id="window1">
<property name="default_width">500</property>
<property name="default_height">500</property>
<signal name="destroy_event" handler="on_window1_destroy_event"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<child>
<widget class="GtkMenuBar" id="menubar1">
<property name="visible">True</property>
<property name="ubuntu_local">True</property>
<child>
<widget class="GtkMenuItem" id="menuitem1">
<property name="visible">True</property>
<property name="label" translatable="yes">_File</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="menu1">
<property name="visible">True</property>
<property name="ubuntu_local">True</property>
<child>
<widget class="GtkImageMenuItem" id="connectMenu">
<property name="label" translatable="yes">Connect to ...</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
<property name="use_stock">False</property>
<child internal-child="image">
<widget class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="stock">gtk-missing-image</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="imagemenuitem5">
<property name="label">gtk-quit</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="menuitem4">
<property name="visible">True</property>
<property name="label" translatable="yes">_Help</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="menu3">
<property name="visible">True</property>
<property name="ubuntu_local">True</property>
<child>
<widget class="GtkImageMenuItem" id="imagemenuitem10">
<property name="label">gtk-about</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkHPaned" id="hpaned1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<child>
<widget class="GtkTextView" id="chatBox">
<property name="width_request">412</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
<signal name="focus" handler="on_chatBox_focus"/>
</widget>
</child>
</widget>
<packing>
<property name="resize">True</property>
<property name="shrink">False</property>
</packing>
</child>
<child>
<widget class="GtkTreeView" id="nicksList">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">1</property>
</widget>
<packing>
<property name="resize">False</property>
<property name="shrink">True</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<child>
<widget class="GtkEntry" id="nickBox">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
<property name="width_chars">10</property>
<property name="text" translatable="yes">Anonymous</property>
<signal name="activate" handler="on_nickBox_activate"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
<signal name="activate" handler="on_entry_activate"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="sendButton">
<property name="label" translatable="yes">Chat!</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_sendButton_clicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkStatusbar" id="statusbar">
<property name="visible">True</property>
<property name="spacing">2</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">3</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

196
games/JS-chat/chat.rb Executable file
View file

@ -0,0 +1,196 @@
#!/usr/bin/env ruby -wKU
$: << "."
require 'ggs-network.rb'
require 'ggs-delegate.rb'
require 'my-random.rb'
require 'readline'
class Chat
include GGSDelegate
include MyRandom
@@log_file_path = "/tmp/ggs-ping-log.csv"
@@bot_threads = []
def initialize(is_bot=false, table_token="")
@is_bot = is_bot
@log = nil
@ignore = false
stty_save = `stty -g`.chomp
trap('INT') { system('stty', stty_save); exit }
print "Table token (empty for new): " unless @is_bot
table_token = gets.chomp unless @is_bot
@ggs_network = GGSNetwork.new(self, table_token)
@ggs_network.connect("192.168.0.2", 9000)
#@ggs_network.connect("localhost", 9000)
end
def ggsNetworkReady(ggs_network, am_i_host)
unless am_i_host
say @ggs_network.table_token
source_code = File.open("chat_server.js", "rb").read
@ggs_network.define(source_code)
else
ggsNetworkDefined(ggs_network, true)
end
end
def ggsNetworkDefined(ggs_network, defined)
if defined
@nick = ""
while @nick == ""
print "\rYour nickname: " unless @is_bot
unless @is_bot
@nick = gets.chomp
else
@nick = random_nick
end
end
@ggs_network.sendCommand("/nick", @nick)
t = Thread.new {
loop do
unless @is_bot
input
else
sleep(rand 2) # interfall for bot to do something
random_function
end
end
}
@@bot_threads << t if @is_bot
else
source_code = File.open("chat_server.js", "rb").read
@ggs_network.define(source_code)
end
end
def ggsNetworkReceivedCommandWithArgs(ggs_network, command, args)
case command
when "message" then message(args)
when "notice" then notice(args)
when "pong" then pong(args)
end
end
protected
def start_bots(number)
number.times { |n|
say "<starting bot #{n}>"
@@bot_threads << Thread.new {
Chat.new(true, @ggs_network.table_token)
}
}
end
def stop_bots
@@bot_threads.each do |bot|
bot.kill
end
@bot_threads = []
say "<stopped all bots>"
end
def message(message)
say message
end
def notice(notice)
say "<#{notice}>"
end
def input(message="")
message = Readline.readline('> ', true) unless @is_bot
if message[0,6] == "/nick "
@nick = message[6..-1]
@ggs_network.sendCommand("/nick", @nick)
elsif message == "/ping"
ping()
elsif message[0,6] == "/bots "
number = message[6..-1].to_i
say "<starting #{number} bots>"
start_bots(number)
elsif message == "/bots"
stop_bots
elsif message == "/log"
toggle_log
elsif message == "/help"
help
elsif message == "/exit"
exit
elsif message == "/ignore"
@ignore = @ignore ? false : true
if @ignore
say "<ignoring on>"
else
say "<ignoring off>"
end
else
@ggs_network.sendCommand("message", message) unless message == ""
end
end
def ping
@start_ping = Time.now
@ggs_network.sendCommand("ping", @ggs_network.player_token)
end
def pong(server_log)
time = ((Time.now - @start_ping) * 1000).to_s
say "<pong: #{time} #{server_log}>"
File.open(@@log_file_path, 'a') {|f| f.write(time << ",#{server_log}\n") } unless @log.nil?
end
def say(something)
unless @ignore or @is_bot
puts "\r#{something}"
print "> "
end
end
def toggle_log
if @log.nil?
say "<starting logging>"
@log = Thread.new {
loop {
sleep 1
ping
}
}
else
@log.kill
@log = nil
say "<stopped logging>"
end
end
def help
puts "+-----------------------------------------------+"
puts "| something | normal message |"
puts "| /nick something | changing your nick |"
puts "| /bots n | start n bots |"
puts "| /bots | stop all bots |"
puts "| /log | toggle logging |"
puts "| /ignore | toggle ignoring everyone |"
puts "| /exit | exit chat |"
puts "| /help | show this help |"
puts "+-----------------------------------------------+"
end
end
if __FILE__ == $0
Chat.new
end

View file

@ -0,0 +1,25 @@
function playerCommand(player_id, command, args) {
if(command == "/nick") {
changeNick(player_id, args);
} else if(command == "message") {
message(player_id, args);
} else if(command == "ping") {
GGS.sendCommand(player_id, "pong", GGS.serverInfo() + "");
}
}
function changeNick(player_id, nick) {
var old_nick = GGS.localStorage.getItem("nick_" + player_id);
GGS.localStorage.setItem("nick_" + player_id, nick);
if (!old_nick) {
GGS.sendCommandToAll("notice", nick + " joined");
} else {
GGS.sendCommandToAll("notice", old_nick + " is now called " + nick);
}
}
function message(player_id, message) {
var nick = GGS.localStorage.getItem("nick_" + player_id);
GGS.sendCommandToAll('message', nick + "> " + message);
}

View file

@ -0,0 +1,15 @@
module GGSDelegate
def ggsNetworkReady(ggs_network, ready)
raise "ggsNetworkReady(ggs_network, ready) must be overridden"
end
def ggsNetworkDefined(ggs_network, defined)
raise "ggsNetworkDefined(ggs_network, defined) must be overridden"
end
def ggsNetworkReceivedCommandWithArgs(ggs_network, command, args)
raise "ggsNetworkReceivedCommandWithArgs(ggs_network, command, args) must be overridden"
end
end

View file

@ -0,0 +1,95 @@
require 'socket'
class GGSNetwork
SERVER = "Server"
CLIENT = "Game"
public
attr_accessor :delegate, :player_token, :table_token
def initialize(delegate, table_token="")
@table_token = table_token
@delegate = delegate
@player_token = nil
end
def define(source_code)
write( makeMessage(SERVER, "define", source_code) )
end
def sendCommand(command, args="")
write( makeMessage(CLIENT, command, args) )
end
def connect(host='localhost', port=9000)
@socket = TCPSocket.new(host, port)
write( makeMessage(SERVER, "hello", @table_token) )
read
end
protected
def write(message)
#puts message
@socket.write(message)
end
def read
loop do
headers = {}
size = 0
args = ""
select([@socket], nil, nil)
while (line = @socket.gets) != "\n"
break if line.nil?
key, value = line.split(": ")
headers[key] = value.strip
end
if headers.has_key?("Content-Size")
args = @socket.read(headers["Content-Size"].to_i)
end
receivedCommand(headers, args)
end
end
def receivedCommand(headers, data)
if headers.has_key? "Client-Command"
command = headers["Client-Command"]
case command
when "hello"
parse_hello(data)
@delegate.ggsNetworkReady(self, @am_i_host)
when "defined"
@delegate.ggsNetworkDefined(self, true)
else
@delegate.ggsNetworkReceivedCommandWithArgs(self, command, data)
end
else
STDERR.print "ERR: " + [headers, data, @socket.inspect].inspect + "\n"
end
end
def makeMessage(serverOrGame, command, args)
message = ""
message += "Token: #{@player_token}\n" unless @player_token.nil?
message += "#{serverOrGame}-Command: #{command}\n" +
"Content-Length: #{args.bytesize}\n\n"
message += args if args.length > 0
message
end
def parse_hello(message)
@player_token, shall_define, @table_token = message.split(",")
@am_i_host = shall_define == "true"
end
end

View file

@ -0,0 +1,27 @@
module MyRandom
def random_function
funcs = []
funcs << lambda { ping() }
funcs << lambda { input("/nick " + random_nick) }
20.times { funcs << lambda { input(random_message) } }
funcs[rnd(0,funcs.length)].call
end
def random_message
random_string(rnd(1,30))
end
def random_nick
random_string(rnd(1,6))
end
def random_string(length)
o = [('a'..'z'),('A'..'Z')].map{|i| i.to_a}.flatten;
(0..length).map{ o[rand(o.length)] }.join;
end
def rnd(min, max)
((rand * (max - min)) + min).to_i
end
end

5
games/Pong-bots/bots.sh Executable file
View file

@ -0,0 +1,5 @@
#!/bin/sh
for (( i = 0; i < 2; i++ )); do
ruby pong-bot.rb &
done

View file

@ -0,0 +1,15 @@
module GGSDelegate
def ggsNetworkReady(ggs_network, ready)
raise "ggsNetworkReady(ggs_network, ready) must be overridden"
end
def ggsNetworkDefined(ggs_network, defined)
raise "ggsNetworkDefined(ggs_network, defined) must be overridden"
end
def ggsNetworkReceivedCommandWithArgs(ggs_network, command, args)
raise "ggsNetworkReceivedCommandWithArgs(ggs_network, command, args) must be overridden"
end
end

View file

@ -0,0 +1,86 @@
require 'socket'
class GGSNetwork
SERVER = "Server"
CLIENT = "Game"
public
attr_accessor :delegate
def initialize(delegate)
@delegate = delegate
end
def define(source_code)
write( makeMessage(SERVER, "define", source_code) )
end
def sendCommand(command, args="")
write( makeMessage(CLIENT, command, args) )
end
def connect(host='localhost', port=9000)
@socket = TCPSocket.new(host, port)
sprintf(@socket)
read
end
protected
def write(message)
puts message.inspect
@socket.write(message)
end
def read
loop do
headers = {}
size = 0
args = ""
select([@socket], nil, nil)
while (line = @socket.gets) != "\n"
break if line.nil?
key, value = line.split(": ")
headers[key] = value.strip
end
if headers.has_key?("Content-Size")
args = @socket.read(headers["Content-Size"].to_i)
end
receivedCommand(headers, args)
end
end
def receivedCommand(headers, data)
puts [headers, data].inspect
if headers.has_key? "Client-Command"
command = headers["Client-Command"]
case command
when "hello"
@game_token = data
@delegate.ggsNetworkReady(self, true)
when "defined"
@delegate.ggsNetworkDefined(self, true)
else
@delegate.ggsNetworkReceivedCommandWithArgs(self, command, data)
end
end
end
def makeMessage(serverOrGame, command, args)
message = "Token: #{@game_token}\n" +
"#{serverOrGame}-Command: #{command}\n" +
"Content-Length: #{args.length}\n\n"
message += args if args.length > 0
message
end
end

114
games/Pong-bots/pong-bot.rb Executable file
View file

@ -0,0 +1,114 @@
#!/usr/bin/env ruby -wKU
$: << "."
require 'ggs-network.rb'
require 'ggs-delegate.rb'
class PongBot
include GGSDelegate
def initialize
@me = nil
@player1 = Pos.new
@player2 = Pos.new
@ball = Pos.new
@game_paused = true
@send_start = false
@ggs_network = GGSNetwork.new(self)
@ggs_network.connect("192.168.0.1")
#@ggs_network.connect()
end
def ggsNetworkReady(ggs_network, ready)
@ggs_network.sendCommand("ready")
end
def ggsNetworkDefined(ggs_network, defined)
# do nothing
end
def ggsNetworkReceivedCommandWithArgs(ggs_network, command, args)
case command
when "welcome" then welcome(args)
when "ball" then ball(args)
when "player1_y" then player1_y(args)
when "player2_y" then player2_y(args)
when "game" then game(args)
when "player1_points" then new_round()
when "player2_points" then new_round()
end
end
protected
def gameTick()
if @game_paused
unless @send_start
@ggs_network.sendCommand("start")
@send_start = true
end
else
if @ball.y < @me.y - 5
@ggs_network.sendCommand("up")
elsif @ball.y > @me.y - 5
@ggs_network.sendCommand("down")
end
end
end
def welcome(who_am_i)
if who_am_i == 1
@me = @player1
else
@me = @player2
end
Thread.new {
loop do
gameTick()
sleep 0.3
end
}
end
def ball(pos_s)
x, y = pos_s.split(",")
@ball.x, @ball.y = x.to_i, y.to_i
end
def player1_y(y)
@player1.y = y.to_i
end
def player2_y(y)
@player2.y = y.to_i
end
def game(wait_or_start)
if wait_or_start == "wait"
else
@game_paused = false
end
end
def new_round
@game_paused = true
@send_start = false
end
class Pos
attr_accessor :x, :y
def initialize
@x = 0
@y = 0
end
end
end
if __FILE__ == $0
PongBot.new
end

3
games/Pong-bots/start.sh Normal file
View file

@ -0,0 +1,3 @@
for ((i = 0; i<30; i++)); do
ruby pong-bot.rb &
done

View file

@ -0,0 +1,659 @@
//
// AsyncSocket.h
//
// This class is in the public domain.
// Originally created by Dustin Voss on Wed Jan 29 2003.
// Updated and maintained by Deusty Designs and the Mac development community.
//
// http://code.google.com/p/cocoaasyncsocket/
//
#import <Foundation/Foundation.h>
@class AsyncSocket;
@class AsyncReadPacket;
@class AsyncWritePacket;
extern NSString *const AsyncSocketException;
extern NSString *const AsyncSocketErrorDomain;
enum AsyncSocketError
{
AsyncSocketCFSocketError = kCFSocketError, // From CFSocketError enum.
AsyncSocketNoError = 0, // Never used.
AsyncSocketCanceledError, // onSocketWillConnect: returned NO.
AsyncSocketConnectTimeoutError,
AsyncSocketReadMaxedOutError, // Reached set maxLength without completing
AsyncSocketReadTimeoutError,
AsyncSocketWriteTimeoutError
};
typedef enum AsyncSocketError AsyncSocketError;
@protocol AsyncSocketDelegate
@optional
/**
* In the event of an error, the socket is closed.
* You may call "unreadData" during this call-back to get the last bit of data off the socket.
* When connecting, this delegate method may be called
* before"onSocket:didAcceptNewSocket:" or "onSocket:didConnectToHost:".
**/
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;
/**
* Called when a socket disconnects with or without error. If you want to release a socket after it disconnects,
* do so here. It is not safe to do that during "onSocket:willDisconnectWithError:".
*
* If you call the disconnect method, and the socket wasn't already disconnected,
* this delegate method will be called before the disconnect method returns.
**/
- (void)onSocketDidDisconnect:(AsyncSocket *)sock;
/**
* Called when a socket accepts a connection. Another socket is spawned to handle it. The new socket will have
* the same delegate and will call "onSocket:didConnectToHost:port:".
**/
- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;
/**
* Called when a new socket is spawned to handle a connection. This method should return the run-loop of the
* thread on which the new socket and its delegate should operate. If omitted, [NSRunLoop currentRunLoop] is used.
**/
- (NSRunLoop *)onSocket:(AsyncSocket *)sock wantsRunLoopForNewSocket:(AsyncSocket *)newSocket;
/**
* Called when a socket is about to connect. This method should return YES to continue, or NO to abort.
* If aborted, will result in AsyncSocketCanceledError.
*
* If the connectToHost:onPort:error: method was called, the delegate will be able to access and configure the
* CFReadStream and CFWriteStream as desired prior to connection.
*
* If the connectToAddress:error: method was called, the delegate will be able to access and configure the
* CFSocket and CFSocketNativeHandle (BSD socket) as desired prior to connection. You will be able to access and
* configure the CFReadStream and CFWriteStream in the onSocket:didConnectToHost:port: method.
**/
- (BOOL)onSocketWillConnect:(AsyncSocket *)sock;
/**
* Called when a socket connects and is ready for reading and writing.
* The host parameter will be an IP address, not a DNS name.
**/
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port;
/**
* Called when a socket has completed reading the requested data into memory.
* Not called if there is an error.
**/
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag;
/**
* Called when a socket has read in data, but has not yet completed the read.
* This would occur if using readToData: or readToLength: methods.
* It may be used to for things such as updating progress bars.
**/
- (void)onSocket:(AsyncSocket *)sock didReadPartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;
/**
* Called when a socket has completed writing the requested data. Not called if there is an error.
**/
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;
/**
* Called when a socket has written some data, but has not yet completed the entire write.
* It may be used to for things such as updating progress bars.
**/
- (void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;
/**
* Called if a read operation has reached its timeout without completing.
* This method allows you to optionally extend the timeout.
* If you return a positive time interval (> 0) the read's timeout will be extended by the given amount.
* If you don't implement this method, or return a non-positive time interval (<= 0) the read will timeout as usual.
*
* The elapsed parameter is the sum of the original timeout, plus any additions previously added via this method.
* The length parameter is the number of bytes that have been read so far for the read operation.
*
* Note that this method may be called multiple times for a single read if you return positive numbers.
**/
- (NSTimeInterval)onSocket:(AsyncSocket *)sock
shouldTimeoutReadWithTag:(long)tag
elapsed:(NSTimeInterval)elapsed
bytesDone:(NSUInteger)length;
/**
* Called if a write operation has reached its timeout without completing.
* This method allows you to optionally extend the timeout.
* If you return a positive time interval (> 0) the write's timeout will be extended by the given amount.
* If you don't implement this method, or return a non-positive time interval (<= 0) the write will timeout as usual.
*
* The elapsed parameter is the sum of the original timeout, plus any additions previously added via this method.
* The length parameter is the number of bytes that have been written so far for the write operation.
*
* Note that this method may be called multiple times for a single write if you return positive numbers.
**/
- (NSTimeInterval)onSocket:(AsyncSocket *)sock
shouldTimeoutWriteWithTag:(long)tag
elapsed:(NSTimeInterval)elapsed
bytesDone:(NSUInteger)length;
/**
* Called after the socket has successfully completed SSL/TLS negotiation.
* This method is not called unless you use the provided startTLS method.
*
* If a SSL/TLS negotiation fails (invalid certificate, etc) then the socket will immediately close,
* and the onSocket:willDisconnectWithError: delegate method will be called with the specific SSL error code.
**/
- (void)onSocketDidSecure:(AsyncSocket *)sock;
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@interface AsyncSocket : NSObject
{
CFSocketNativeHandle theNativeSocket4;
CFSocketNativeHandle theNativeSocket6;
CFSocketRef theSocket4; // IPv4 accept or connect socket
CFSocketRef theSocket6; // IPv6 accept or connect socket
CFReadStreamRef theReadStream;
CFWriteStreamRef theWriteStream;
CFRunLoopSourceRef theSource4; // For theSocket4
CFRunLoopSourceRef theSource6; // For theSocket6
CFRunLoopRef theRunLoop;
CFSocketContext theContext;
NSArray *theRunLoopModes;
NSTimer *theConnectTimer;
NSMutableArray *theReadQueue;
AsyncReadPacket *theCurrentRead;
NSTimer *theReadTimer;
NSMutableData *partialReadBuffer;
NSMutableArray *theWriteQueue;
AsyncWritePacket *theCurrentWrite;
NSTimer *theWriteTimer;
id theDelegate;
UInt16 theFlags;
long theUserData;
}
- (id)init;
- (id)initWithDelegate:(id)delegate;
- (id)initWithDelegate:(id)delegate userData:(long)userData;
/* String representation is long but has no "\n". */
- (NSString *)description;
/**
* Use "canSafelySetDelegate" to see if there is any pending business (reads and writes) with the current delegate
* before changing it. It is, of course, safe to change the delegate before connecting or accepting connections.
**/
- (id)delegate;
- (BOOL)canSafelySetDelegate;
- (void)setDelegate:(id)delegate;
/* User data can be a long, or an id or void * cast to a long. */
- (long)userData;
- (void)setUserData:(long)userData;
/* Don't use these to read or write. And don't close them either! */
- (CFSocketRef)getCFSocket;
- (CFReadStreamRef)getCFReadStream;
- (CFWriteStreamRef)getCFWriteStream;
// Once one of the accept or connect methods are called, the AsyncSocket instance is locked in
// and the other accept/connect methods can't be called without disconnecting the socket first.
// If the attempt fails or times out, these methods either return NO or
// call "onSocket:willDisconnectWithError:" and "onSockedDidDisconnect:".
// When an incoming connection is accepted, AsyncSocket invokes several delegate methods.
// These methods are (in chronological order):
// 1. onSocket:didAcceptNewSocket:
// 2. onSocket:wantsRunLoopForNewSocket:
// 3. onSocketWillConnect:
//
// Your server code will need to retain the accepted socket (if you want to accept it).
// The best place to do this is probably in the onSocket:didAcceptNewSocket: method.
//
// After the read and write streams have been setup for the newly accepted socket,
// the onSocket:didConnectToHost:port: method will be called on the proper run loop.
//
// Multithreading Note: If you're going to be moving the newly accepted socket to another run
// loop by implementing onSocket:wantsRunLoopForNewSocket:, then you should wait until the
// onSocket:didConnectToHost:port: method before calling read, write, or startTLS methods.
// Otherwise read/write events are scheduled on the incorrect runloop, and chaos may ensue.
/**
* Tells the socket to begin listening and accepting connections on the given port.
* When a connection comes in, the AsyncSocket instance will call the various delegate methods (see above).
* The socket will listen on all available interfaces (e.g. wifi, ethernet, etc)
**/
- (BOOL)acceptOnPort:(UInt16)port error:(NSError **)errPtr;
/**
* This method is the same as acceptOnPort:error: with the additional option
* of specifying which interface to listen on. So, for example, if you were writing code for a server that
* has multiple IP addresses, you could specify which address you wanted to listen on. Or you could use it
* to specify that the socket should only accept connections over ethernet, and not other interfaces such as wifi.
* You may also use the special strings "localhost" or "loopback" to specify that
* the socket only accept connections from the local machine.
*
* To accept connections on any interface pass nil, or simply use the acceptOnPort:error: method.
**/
- (BOOL)acceptOnInterface:(NSString *)interface port:(UInt16)port error:(NSError **)errPtr;
/**
* Connects to the given host and port.
* The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2")
**/
- (BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr;
/**
* This method is the same as connectToHost:onPort:error: with an additional timeout option.
* To not time out use a negative time interval, or simply use the connectToHost:onPort:error: method.
**/
- (BOOL)connectToHost:(NSString *)hostname
onPort:(UInt16)port
withTimeout:(NSTimeInterval)timeout
error:(NSError **)errPtr;
/**
* Connects to the given address, specified as a sockaddr structure wrapped in a NSData object.
* For example, a NSData object returned from NSNetservice's addresses method.
*
* If you have an existing struct sockaddr you can convert it to a NSData object like so:
* struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
* struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];
**/
- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
/**
* This method is the same as connectToAddress:error: with an additional timeout option.
* To not time out use a negative time interval, or simply use the connectToAddress:error: method.
**/
- (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr;
- (BOOL)connectToAddress:(NSData *)remoteAddr
viaInterfaceAddress:(NSData *)interfaceAddr
withTimeout:(NSTimeInterval)timeout
error:(NSError **)errPtr;
/**
* Disconnects immediately. Any pending reads or writes are dropped.
* If the socket is not already disconnected, the onSocketDidDisconnect delegate method
* will be called immediately, before this method returns.
*
* Please note the recommended way of releasing an AsyncSocket instance (e.g. in a dealloc method)
* [asyncSocket setDelegate:nil];
* [asyncSocket disconnect];
* [asyncSocket release];
**/
- (void)disconnect;
/**
* Disconnects after all pending reads have completed.
* After calling this, the read and write methods will do nothing.
* The socket will disconnect even if there are still pending writes.
**/
- (void)disconnectAfterReading;
/**
* Disconnects after all pending writes have completed.
* After calling this, the read and write methods will do nothing.
* The socket will disconnect even if there are still pending reads.
**/
- (void)disconnectAfterWriting;
/**
* Disconnects after all pending reads and writes have completed.
* After calling this, the read and write methods will do nothing.
**/
- (void)disconnectAfterReadingAndWriting;
/* Returns YES if the socket and streams are open, connected, and ready for reading and writing. */
- (BOOL)isConnected;
/**
* Returns the local or remote host and port to which this socket is connected, or nil and 0 if not connected.
* The host will be an IP address.
**/
- (NSString *)connectedHost;
- (UInt16)connectedPort;
- (NSString *)localHost;
- (UInt16)localPort;
/**
* Returns the local or remote address to which this socket is connected,
* specified as a sockaddr structure wrapped in a NSData object.
*
* See also the connectedHost, connectedPort, localHost and localPort methods.
**/
- (NSData *)connectedAddress;
- (NSData *)localAddress;
/**
* Returns whether the socket is IPv4 or IPv6.
* An accepting socket may be both.
**/
- (BOOL)isIPv4;
- (BOOL)isIPv6;
// The readData and writeData methods won't block (they are asynchronous).
//
// When a read is complete the onSocket:didReadData:withTag: delegate method is called.
// When a write is complete the onSocket:didWriteDataWithTag: delegate method is called.
//
// You may optionally set a timeout for any read/write operation. (To not timeout, use a negative time interval.)
// If a read/write opertion times out, the corresponding "onSocket:shouldTimeout..." delegate method
// is called to optionally allow you to extend the timeout.
// Upon a timeout, the "onSocket:willDisconnectWithError:" method is called, followed by "onSocketDidDisconnect".
//
// The tag is for your convenience.
// You can use it as an array index, step number, state id, pointer, etc.
/**
* Reads the first available bytes that become available on the socket.
*
* If the timeout value is negative, the read operation will not use a timeout.
**/
- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag;
/**
* Reads the first available bytes that become available on the socket.
* The bytes will be appended to the given byte buffer starting at the given offset.
* The given buffer will automatically be increased in size if needed.
*
* If the timeout value is negative, the read operation will not use a timeout.
* If the buffer if nil, the socket will create a buffer for you.
*
* If the bufferOffset is greater than the length of the given buffer,
* the method will do nothing, and the delegate will not be called.
*
* If you pass a buffer, you must not alter it in any way while AsyncSocket is using it.
* After completion, the data returned in onSocket:didReadData:withTag: will be a subset of the given buffer.
* That is, it will reference the bytes that were appended to the given buffer.
**/
- (void)readDataWithTimeout:(NSTimeInterval)timeout
buffer:(NSMutableData *)buffer
bufferOffset:(NSUInteger)offset
tag:(long)tag;
/**
* Reads the first available bytes that become available on the socket.
* The bytes will be appended to the given byte buffer starting at the given offset.
* The given buffer will automatically be increased in size if needed.
* A maximum of length bytes will be read.
*
* If the timeout value is negative, the read operation will not use a timeout.
* If the buffer if nil, a buffer will automatically be created for you.
* If maxLength is zero, no length restriction is enforced.
*
* If the bufferOffset is greater than the length of the given buffer,
* the method will do nothing, and the delegate will not be called.
*
* If you pass a buffer, you must not alter it in any way while AsyncSocket is using it.
* After completion, the data returned in onSocket:didReadData:withTag: will be a subset of the given buffer.
* That is, it will reference the bytes that were appended to the given buffer.
**/
- (void)readDataWithTimeout:(NSTimeInterval)timeout
buffer:(NSMutableData *)buffer
bufferOffset:(NSUInteger)offset
maxLength:(NSUInteger)length
tag:(long)tag;
/**
* Reads the given number of bytes.
*
* If the timeout value is negative, the read operation will not use a timeout.
*
* If the length is 0, this method does nothing and the delegate is not called.
**/
- (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
/**
* Reads the given number of bytes.
* The bytes will be appended to the given byte buffer starting at the given offset.
* The given buffer will automatically be increased in size if needed.
*
* If the timeout value is negative, the read operation will not use a timeout.
* If the buffer if nil, a buffer will automatically be created for you.
*
* If the length is 0, this method does nothing and the delegate is not called.
* If the bufferOffset is greater than the length of the given buffer,
* the method will do nothing, and the delegate will not be called.
*
* If you pass a buffer, you must not alter it in any way while AsyncSocket is using it.
* After completion, the data returned in onSocket:didReadData:withTag: will be a subset of the given buffer.
* That is, it will reference the bytes that were appended to the given buffer.
**/
- (void)readDataToLength:(NSUInteger)length
withTimeout:(NSTimeInterval)timeout
buffer:(NSMutableData *)buffer
bufferOffset:(NSUInteger)offset
tag:(long)tag;
/**
* Reads bytes until (and including) the passed "data" parameter, which acts as a separator.
*
* If the timeout value is negative, the read operation will not use a timeout.
*
* If you pass nil or zero-length data as the "data" parameter,
* the method will do nothing, and the delegate will not be called.
*
* To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter.
* Note that this method is not character-set aware, so if a separator can occur naturally as part of the encoding for
* a character, the read will prematurely end.
**/
- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
/**
* Reads bytes until (and including) the passed "data" parameter, which acts as a separator.
* The bytes will be appended to the given byte buffer starting at the given offset.
* The given buffer will automatically be increased in size if needed.
*
* If the timeout value is negative, the read operation will not use a timeout.
* If the buffer if nil, a buffer will automatically be created for you.
*
* If the bufferOffset is greater than the length of the given buffer,
* the method will do nothing, and the delegate will not be called.
*
* If you pass a buffer, you must not alter it in any way while AsyncSocket is using it.
* After completion, the data returned in onSocket:didReadData:withTag: will be a subset of the given buffer.
* That is, it will reference the bytes that were appended to the given buffer.
*
* To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter.
* Note that this method is not character-set aware, so if a separator can occur naturally as part of the encoding for
* a character, the read will prematurely end.
**/
- (void)readDataToData:(NSData *)data
withTimeout:(NSTimeInterval)timeout
buffer:(NSMutableData *)buffer
bufferOffset:(NSUInteger)offset
tag:(long)tag;
/**
* Reads bytes until (and including) the passed "data" parameter, which acts as a separator.
*
* If the timeout value is negative, the read operation will not use a timeout.
*
* If maxLength is zero, no length restriction is enforced.
* Otherwise if maxLength bytes are read without completing the read,
* it is treated similarly to a timeout - the socket is closed with a AsyncSocketReadMaxedOutError.
* The read will complete successfully if exactly maxLength bytes are read and the given data is found at the end.
*
* If you pass nil or zero-length data as the "data" parameter,
* the method will do nothing, and the delegate will not be called.
* If you pass a maxLength parameter that is less than the length of the data parameter,
* the method will do nothing, and the delegate will not be called.
*
* To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter.
* Note that this method is not character-set aware, so if a separator can occur naturally as part of the encoding for
* a character, the read will prematurely end.
**/
- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(NSUInteger)length tag:(long)tag;
/**
* Reads bytes until (and including) the passed "data" parameter, which acts as a separator.
* The bytes will be appended to the given byte buffer starting at the given offset.
* The given buffer will automatically be increased in size if needed.
* A maximum of length bytes will be read.
*
* If the timeout value is negative, the read operation will not use a timeout.
* If the buffer if nil, a buffer will automatically be created for you.
*
* If maxLength is zero, no length restriction is enforced.
* Otherwise if maxLength bytes are read without completing the read,
* it is treated similarly to a timeout - the socket is closed with a AsyncSocketReadMaxedOutError.
* The read will complete successfully if exactly maxLength bytes are read and the given data is found at the end.
*
* If you pass a maxLength parameter that is less than the length of the data parameter,
* the method will do nothing, and the delegate will not be called.
* If the bufferOffset is greater than the length of the given buffer,
* the method will do nothing, and the delegate will not be called.
*
* If you pass a buffer, you must not alter it in any way while AsyncSocket is using it.
* After completion, the data returned in onSocket:didReadData:withTag: will be a subset of the given buffer.
* That is, it will reference the bytes that were appended to the given buffer.
*
* To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter.
* Note that this method is not character-set aware, so if a separator can occur naturally as part of the encoding for
* a character, the read will prematurely end.
**/
- (void)readDataToData:(NSData *)data
withTimeout:(NSTimeInterval)timeout
buffer:(NSMutableData *)buffer
bufferOffset:(NSUInteger)offset
maxLength:(NSUInteger)length
tag:(long)tag;
/**
* Writes data to the socket, and calls the delegate when finished.
*
* If you pass in nil or zero-length data, this method does nothing and the delegate will not be called.
* If the timeout value is negative, the write operation will not use a timeout.
**/
- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
/**
* Returns progress of current read or write, from 0.0 to 1.0, or NaN if no read/write (use isnan() to check).
* "tag", "done" and "total" will be filled in if they aren't NULL.
**/
- (float)progressOfReadReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;
- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;
/**
* Secures the connection using SSL/TLS.
*
* This method may be called at any time, and the TLS handshake will occur after all pending reads and writes
* are finished. This allows one the option of sending a protocol dependent StartTLS message, and queuing
* the upgrade to TLS at the same time, without having to wait for the write to finish.
* Any reads or writes scheduled after this method is called will occur over the secured connection.
*
* The possible keys and values for the TLS settings are well documented.
* Some possible keys are:
* - kCFStreamSSLLevel
* - kCFStreamSSLAllowsExpiredCertificates
* - kCFStreamSSLAllowsExpiredRoots
* - kCFStreamSSLAllowsAnyRoot
* - kCFStreamSSLValidatesCertificateChain
* - kCFStreamSSLPeerName
* - kCFStreamSSLCertificates
* - kCFStreamSSLIsServer
*
* Please refer to Apple's documentation for associated values, as well as other possible keys.
*
* If you pass in nil or an empty dictionary, the default settings will be used.
*
* The default settings will check to make sure the remote party's certificate is signed by a
* trusted 3rd party certificate agency (e.g. verisign) and that the certificate is not expired.
* However it will not verify the name on the certificate unless you
* give it a name to verify against via the kCFStreamSSLPeerName key.
* The security implications of this are important to understand.
* Imagine you are attempting to create a secure connection to MySecureServer.com,
* but your socket gets directed to MaliciousServer.com because of a hacked DNS server.
* If you simply use the default settings, and MaliciousServer.com has a valid certificate,
* the default settings will not detect any problems since the certificate is valid.
* To properly secure your connection in this particular scenario you
* should set the kCFStreamSSLPeerName property to "MySecureServer.com".
* If you do not know the peer name of the remote host in advance (for example, you're not sure
* if it will be "domain.com" or "www.domain.com"), then you can use the default settings to validate the
* certificate, and then use the X509Certificate class to verify the issuer after the socket has been secured.
* The X509Certificate class is part of the CocoaAsyncSocket open source project.
**/
- (void)startTLS:(NSDictionary *)tlsSettings;
/**
* For handling readDataToData requests, data is necessarily read from the socket in small increments.
* The performance can be much improved by allowing AsyncSocket to read larger chunks at a time and
* store any overflow in a small internal buffer.
* This is termed pre-buffering, as some data may be read for you before you ask for it.
* If you use readDataToData a lot, enabling pre-buffering will result in better performance, especially on the iPhone.
*
* The default pre-buffering state is controlled by the DEFAULT_PREBUFFERING definition.
* It is highly recommended one leave this set to YES.
*
* This method exists in case pre-buffering needs to be disabled by default for some unforeseen reason.
* In that case, this method exists to allow one to easily enable pre-buffering when ready.
**/
- (void)enablePreBuffering;
/**
* When you create an AsyncSocket, it is added to the runloop of the current thread.
* So for manually created sockets, it is easiest to simply create the socket on the thread you intend to use it.
*
* If a new socket is accepted, the delegate method onSocket:wantsRunLoopForNewSocket: is called to
* allow you to place the socket on a separate thread. This works best in conjunction with a thread pool design.
*
* If, however, you need to move the socket to a separate thread at a later time, this
* method may be used to accomplish the task.
*
* This method must be called from the thread/runloop the socket is currently running on.
*
* Note: After calling this method, all further method calls to this object should be done from the given runloop.
* Also, all delegate calls will be sent on the given runloop.
**/
- (BOOL)moveToRunLoop:(NSRunLoop *)runLoop;
/**
* Allows you to configure which run loop modes the socket uses.
* The default set of run loop modes is NSDefaultRunLoopMode.
*
* If you'd like your socket to continue operation during other modes, you may want to add modes such as
* NSModalPanelRunLoopMode or NSEventTrackingRunLoopMode. Or you may simply want to use NSRunLoopCommonModes.
*
* Accepted sockets will automatically inherit the same run loop modes as the listening socket.
*
* Note: NSRunLoopCommonModes is defined in 10.5. For previous versions one can use kCFRunLoopCommonModes.
**/
- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;
- (BOOL)addRunLoopMode:(NSString *)runLoopMode;
- (BOOL)removeRunLoopMode:(NSString *)runLoopMode;
/**
* Returns the current run loop modes the AsyncSocket instance is operating in.
* The default set of run loop modes is NSDefaultRunLoopMode.
**/
- (NSArray *)runLoopModes;
/**
* In the event of an error, this method may be called during onSocket:willDisconnectWithError: to read
* any data that's left on the socket.
**/
- (NSData *)unreadData;
/* A few common line separators, for use with the readDataToData:... methods. */
+ (NSData *)CRLFData; // 0x0D0A
+ (NSData *)CRData; // 0x0D
+ (NSData *)LFData; // 0x0A
+ (NSData *)ZeroData; // 0x00
@end

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,21 @@
//
// GGSDelegate.h
// Pong
//
// Created by Jeena on 27.02.11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "GGSNetwork.h"
@class GGSNetwork;
@protocol GGSDelegate
- (void)GGSNetwork:(GGSNetwork *)ggsNetwork ready:(BOOL)ready;
- (void)GGSNetwork:(GGSNetwork *)ggsNetwork defined:(BOOL)defined;
- (void)GGSNetwork:(GGSNetwork *)ggsNetwork receivedCommand:(NSString *)command withArgs:(NSString *)args;
- (void)GGSNetwork:(GGSNetwork *)ggsNetwork defined:(BOOL)defined;
@end

View file

@ -0,0 +1,34 @@
//
// Network.h
// Pong
//
// Created by Jeena on 27.02.11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "AsyncSocket.h"
#import "GGSDelegate.h"
@protocol GGSDelegate;
@interface GGSNetwork : NSObject {
AsyncSocket *asyncSocket;
id<GGSDelegate> delegate;
NSString *gameToken;
NSDictionary *currentHeaders;
}
@property (nonatomic, retain) AsyncSocket *asyncSocket;
@property (nonatomic, retain) id<GGSDelegate> delegate;
@property (nonatomic, retain) NSDictionary *currentHeaders;
@property (nonatomic, retain) NSString *gameToken;
- (id)initWithDelegate:(id)delegate;
- (NSData *)makeMessageFor:(NSString *)serverOrGame withCommand:(NSString *)command andArgs:(NSString *)args;
- (void)parseAndSetHeader:(NSData *)headerData;
- (void)define:(NSString *)sourceCode;
- (void)sendCommand:(NSString *)command withArgs:(NSString *)args;
@end

View file

@ -0,0 +1,140 @@
//
// Network.m
// Pong
//
// Created by Jeena on 27.02.11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "GGSNetwork.h"
@implementation GGSNetwork
#define GGS_HOST @"ggs.jeena.net"
#define GGS_PORT 9000
#define NO_TIMEOUT -1
#define HEADER_DELIMITER [@"\n\n" dataUsingEncoding:NSUTF8StringEncoding]
#define NO_TAG 7
#define CONNECT_HEAD 8
#define CONNECT_BODY 9
#define HELLO_HEAD 10
#define HELLO_BODY 11
#define DEFINE_HEAD 12
#define DEFINE_BODY 13
#define COMMAND_HEAD 14
#define COMMAND_BODY 15
#define HEAD 16
#define BODY 17
@synthesize asyncSocket, delegate, gameToken, currentHeaders;
- (id)initWithDelegate:(id<GGSDelegate>)_delegate {
if ((self = [super init])) {
delegate = _delegate;
asyncSocket = [[AsyncSocket alloc] initWithDelegate:self];
[asyncSocket connectToHost:GGS_HOST onPort:GGS_PORT error:nil];
[asyncSocket readDataToData:HEADER_DELIMITER withTimeout:NO_TIMEOUT tag:HEAD];
}
return self;
}
- (NSData *)makeMessageFor:(NSString *)serverOrGame withCommand:(NSString *)command andArgs:(NSString *)args {
return [[NSString stringWithFormat:@"Token: %@\n%@-Command: %@\nContent-Length: %i\n\n%@",
self.gameToken,
serverOrGame,
command,
[args length],
args] dataUsingEncoding:NSUTF8StringEncoding];
}
- (void)define:(NSString *)sourceCode {
[asyncSocket writeData:[self makeMessageFor:@"Server" withCommand:@"define" andArgs:sourceCode] withTimeout:NO_TIMEOUT tag:NO_TAG];
}
- (void)sendCommand:(NSString *)command withArgs:(NSString *)args {
[asyncSocket writeData:[self makeMessageFor:@"Game" withCommand:command andArgs:args] withTimeout:NO_TIMEOUT tag:NO_TAG];
}
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port {
//[asyncSocket writeData:[self makeMessageFor:@"Server" withCommand:@"hello" andArgs:@""] withTimeout:NO_TIMEOUT tag:NO_TAG];
}
- (void)onSocket:(AsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag {
if (tag == HEAD) {
[self parseAndSetHeader:data];
NSInteger size = [[self.currentHeaders objectForKey:@"Content-Size"] intValue];
if (size > 0) {
[asyncSocket readDataToLength:size withTimeout:NO_TIMEOUT tag:BODY];
} else {
[delegate GGSNetwork:self receivedCommand:[self.currentHeaders objectForKey:@"Client-Command"] withArgs:@""];
[asyncSocket readDataToData:HEADER_DELIMITER withTimeout:NO_TIMEOUT tag:HEAD];
}
} else {
NSString *response = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSString *command = [self.currentHeaders objectForKey:@"Client-Command"];
if ([command isEqualToString:@"defined"]) {
if ([response isEqualToString:@"ok"]) {
[delegate GGSNetwork:self defined:YES];
} else {
[delegate GGSNetwork:self defined:NO];
}
} else if ([command isEqualToString:@"hello"]) {
self.gameToken = response;
[delegate GGSNetwork:self ready:YES];
NSLog(@"%@", self.gameToken);
} else {
[delegate GGSNetwork:self receivedCommand:command withArgs:response];
}
[asyncSocket readDataToData:HEADER_DELIMITER withTimeout:NO_TIMEOUT tag:HEAD];
}
}
- (void)parseAndSetHeader:(NSData *)headerData {
NSString *headerString = [[NSString alloc] initWithData:headerData encoding:NSUTF8StringEncoding];
NSArray *headers = [headerString componentsSeparatedByString:@"\n"];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:[headers count]];
for (NSInteger i=0; i<[headers count]; i++) {
NSString *header = [headers objectAtIndex:i];
if ([header rangeOfString:@"Client-Command: "].location == 0) {
[dict setValue:[header substringFromIndex:16] forKey:@"Client-Command"];
} else if ([header rangeOfString:@"Content-Size: "].location == 0) {
[dict setValue:[header substringFromIndex:14] forKey:@"Content-Size"];
}
}
self.currentHeaders = dict;
[headerString release];
[dict release];
}
- (void)dealloc {
[asyncSocket release];
[gameToken release];
[currentHeaders release];
[super dealloc];
}
@end

View file

@ -0,0 +1,22 @@
//
// PongAppDelegate.h
// Pong
//
// Created by Jeena on 26.01.11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
@class PongViewController;
@interface PongAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
PongViewController *viewController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet PongViewController *viewController;
@end

View file

@ -0,0 +1,89 @@
//
// PongAppDelegate.m
// Pong
//
// Created by Jeena on 26.01.11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "PongAppDelegate.h"
#import "PongViewController.h"
@implementation PongAppDelegate
@synthesize window;
@synthesize viewController;
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Add the view controller's view to the window and display.
[self.window addSubview:viewController.view];
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
/*
Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
*/
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
*/
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
/*
Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background.
*/
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
[(PongViewController *)viewController restart];
}
- (void)applicationWillTerminate:(UIApplication *)application {
/*
Called when the application is about to terminate.
See also applicationDidEnterBackground:.
*/
}
#pragma mark -
#pragma mark Memory management
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
/*
Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later.
*/
}
- (void)dealloc {
[viewController release];
[window release];
[super dealloc];
}
@end

View file

@ -0,0 +1,58 @@
//
// PongViewController.h
// Pong
//
// Created by Jeena on 26.01.11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "GGSDelegate.h"
#import "GGSNetwork.h"
#import <AVFoundation/AVAudioPlayer.h>
enum GameType {
kGameTypeSinglePlayer = 0,
kGameTypeMultiPlayer,
kGameTypeNetworkMultiPlayer
};
@interface PongViewController : UIViewController <GGSDelegate> {
IBOutlet UIView *ballView;
IBOutlet UIView *player1View;
IBOutlet UIView *player2View;
IBOutlet UILabel *tapToBegin;
CGPoint ballVelocity;
BOOL gamePaused;
IBOutlet UILabel *pointsP1;
IBOutlet UILabel *pointsP2;
GGSNetwork *ggsNetwork;
AVAudioPlayer *pingSound;
AVAudioPlayer *pongSound;
AVAudioPlayer *lostSound;
}
@property (nonatomic, retain) IBOutlet UIView *ballView;
@property (nonatomic, retain) IBOutlet UIView *player1View;
@property (nonatomic, retain) IBOutlet UIView *player2View;
@property (nonatomic, retain) IBOutlet UIView *tapToBegin;
@property (nonatomic, retain) IBOutlet UILabel *pointsP1;
@property (nonatomic, retain) IBOutlet UILabel *pointsP2;
@property (nonatomic, retain) GGSNetwork *ggsNetwork;
- (void)restart;
- (void)startPositions;
- (void)zeroPoints;
- (void)moveBall;
- (void)positionPlayer:(CGPoint)point;
@end

View file

@ -0,0 +1,345 @@
//
// PongViewController.m
// Pong
//
// Created by Jeena on 26.01.11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "PongViewController.h"
@implementation PongViewController
#define PLAYER_SPEED 20
#define BALL_SPEED_X 7
#define BALL_SPEED_Y 5
#define INTERVAL 0.05
#define WIDTH 480
#define HEIGHT 320
#define TOX(x) ( 4.8 * x )
#define TOY(y) ( 3.2 * y )
@synthesize ballView, player1View, player2View, tapToBegin, pointsP1, pointsP2, ggsNetwork;
/*
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
*/
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
[super loadView];
}
*/
-(BOOL)canBecomeFirstResponder {
return YES;
}
#pragma mark -
#pragma mark GGSNetwork Delegate
- (void)GGSNetwork:(GGSNetwork *)_ggsNetwork ready:(BOOL)ready {
NSLog(@"ready");
[ggsNetwork sendCommand:@"ready" withArgs:@""];
}
- (void)GGSNetwork:(GGSNetwork *)_ggsNetwork defined:(BOOL)defined {
// do nothing.
}
- (void)GGSNetwork:(GGSNetwork *)_ggsNetwork receivedCommand:(NSString *)command withArgs:(NSString *)args {
if ([command isEqualToString:@"ball"]) {
NSArray *ball = [args componentsSeparatedByString:@","];
[UIView beginAnimations:NULL context:NULL];
CGFloat x = [[ball objectAtIndex:0] floatValue];
CGFloat y = [[ball objectAtIndex:1] floatValue];
ballView.center = CGPointMake(TOX(x), TOY(y));
[UIView commitAnimations];
} else if ([command isEqualToString:@"player1_y"]) {
[UIView beginAnimations:NULL context:NULL];
player1View.center = CGPointMake(25, TOY([args floatValue]));
[UIView commitAnimations];
} else if ([command isEqualToString:@"player2_y"]) {
[UIView beginAnimations:NULL context:NULL];
player2View.center = CGPointMake(WIDTH - 35, TOY([args floatValue]));
[UIView commitAnimations];
} else if ([command isEqualToString:@"player1_points"]) {
pointsP1.text = args;
gamePaused = YES;
[lostSound play];
} else if ([command isEqualToString:@"player2_points"]) {
pointsP2.text = args;
gamePaused = YES;
[lostSound play];
} else if ([command isEqualToString:@"game"]) {
if ([args isEqualToString:@"wait"]) {
NSLog(@"Other ready");
} else if ([args isEqualToString:@"start"]) {
gamePaused = NO;
}
} else if ([command isEqualToString:@"welcome"]) {
if ([args isEqualToString:@"1"]) {
player1View.backgroundColor = [UIColor redColor];
} else {
player2View.backgroundColor = [UIColor redColor];
}
} else if ([command isEqualToString:@"sound"]) {
if ([args isEqualToString:@"ping"]) {
[pingSound play];
} else {
[pongSound play];
}
}
}
#pragma mark -
#pragma mark Input
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (gamePaused) {
[ggsNetwork sendCommand:@"start" withArgs:@""];
tapToBegin.hidden = YES;
} else {
CGPoint point = [[[touches allObjects] objectAtIndex:0] locationInView:self.view];
if (point.y > (HEIGHT / 2)) {
[ggsNetwork sendCommand:@"down" withArgs:@""];
} else {
[ggsNetwork sendCommand:@"up" withArgs:@""];
}
}
}
#pragma mark -
#pragma mark View
- (void)restart {
player1View.backgroundColor = [UIColor whiteColor];
player2View.backgroundColor = [UIColor whiteColor];
pointsP1.text = @"0";
pointsP2.text = @"0";
self.ggsNetwork = [[GGSNetwork alloc] initWithDelegate:self];
gamePaused = YES;
tapToBegin.hidden = NO;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:@"ping" ofType:@"wav"];
pingSound = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[pingSound play];
path = [[NSBundle mainBundle] pathForResource:@"pong" ofType:@"wav"];
pongSound = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[pongSound play];
path = [[NSBundle mainBundle] pathForResource:@"lost" ofType:@"wav"];
lostSound = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[lostSound play];
//ggsNetwork = [[GGSNetwork alloc] initWithDelegate:self];
gamePaused = YES;
//[self startPositions];
//[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(moveBall) userInfo:nil repeats:YES];
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
/*
# pragma mark -
# pragma mark Ball
- (void)moveBall {
if (!gamePaused) {
[UIView beginAnimations:NULL context:NULL];
ballView.center = CGPointMake(ballView.center.x + ballVelocity.x, ballView.center.y + ballVelocity.y );
[UIView commitAnimations];
if (ballView.center.y > HEIGHT || ballView.center.y < 0) {
ballVelocity.y = -ballVelocity.y;
}
if (CGRectIntersectsRect(ballView.frame, player1View.frame) || CGRectIntersectsRect(ballView.frame, player2View.frame)) {
ballVelocity.x = - (ballVelocity.x + 1);
if (arc4random() % 2) {
ballVelocity.y = - (ballVelocity.y + 1);
}
}
if (ballView.center.x > WIDTH || ballView.center.x < 0) {
if (ballView.center.x < 0) {
pointsP1.text = [NSString stringWithFormat:@"%i", [pointsP1.text intValue] + 1];
} else {
pointsP2.text = [NSString stringWithFormat:@"%i", [pointsP2.text intValue] + 1];
}
gamePaused = YES;
[self startPositions];
}
} else {
tapToBegin.hidden = NO;
}
}
# pragma mark -
# pragma mark Positioning
- (void)startPositions {
int s1 = - (arc4random() % 5);
int s2 = - (arc4random() % 5);
int d1 = arc4random() % 2 ? -1 : 1;
int d2 = arc4random() % 2 ? -1 : 1;
ballVelocity = CGPointMake((BALL_SPEED_X + s1) * d1 , (BALL_SPEED_Y + s2) * d2);
ballView.center = CGPointMake(WIDTH/2, HEIGHT/2);
player1View.center = CGPointMake(30, HEIGHT/2);
player2View.center = CGPointMake(WIDTH-30, HEIGHT/2);
}
- (void)positionPlayer:(CGPoint)point {
UIView *p;
NSInteger direction = 0;
if (point.x < WIDTH/2) {
p = player1View;
} else {
p = player2View;
}
if (point.y > HEIGHT/2 && p.frame.origin.y + p.frame.size.height < HEIGHT) {
direction = 1;
} else if (point.y < HEIGHT/2 && p.frame.origin.y > 0) {
direction = -1;
} else {
direction = 0;
}
CGRect f = p.frame;
f.origin.y = f.origin.y + (PLAYER_SPEED * direction);
[UIView beginAnimations:NULL context:NULL];
p.frame = f;
[UIView commitAnimations];
}
#pragma mark -
#pragma mark Input
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (gamePaused) {
tapToBegin.hidden = YES;
gamePaused = NO;
} else {
switch ([touches count]) {
case 1:
[self positionPlayer:[[[touches allObjects] objectAtIndex:0] locationInView:self.view]];
break;
default:
[self positionPlayer:[[[touches allObjects] objectAtIndex:0] locationInView:self.view]];
[self positionPlayer:[[[touches allObjects] objectAtIndex:1] locationInView:self.view]];
break;
}
}
}
# pragma mark -
# pragma mark Reset
-(BOOL)canBecomeFirstResponder {
return YES;
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.type == UIEventSubtypeMotionShake) {
[self zeroPoints];
}
}
- (void)zeroPoints {
pointsP1.text = @"0";
pointsP2.text = @"0";
}
*/
# pragma mark -
# pragma mark Dealloc
- (void)dealloc {
[ballView release];
[player1View release];
[player2View release];
[tapToBegin release];
[pointsP1 release];
[pointsP2 release];
[ggsNetwork release];
[pingSound release];
[pongSound release];
[lostSound release];
[super dealloc];
}
@end

444
games/Pong/MainWindow.xib Normal file
View file

@ -0,0 +1,444 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
<data>
<int key="IBDocument.SystemTarget">1024</int>
<string key="IBDocument.SystemVersion">10D571</string>
<string key="IBDocument.InterfaceBuilderVersion">786</string>
<string key="IBDocument.AppKitVersion">1038.29</string>
<string key="IBDocument.HIToolboxVersion">460.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">112</string>
</object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
<integer value="10"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</object>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys" id="0">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBProxyObject" id="841351856">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBProxyObject" id="427554174">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBUICustomObject" id="664661524">
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBUIViewController" id="943309135">
<string key="IBUINibName">PongViewController</string>
<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
<object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
<int key="interfaceOrientation">1</int>
</object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBUIHorizontal">NO</bool>
</object>
<object class="IBUIWindow" id="117978783">
<nil key="NSNextResponder"/>
<int key="NSvFlags">292</int>
<string key="NSFrameSize">{320, 480}</string>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MSAxIDEAA</bytes>
</object>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBUIResizesToFullScreen">YES</bool>
</object>
</object>
<object class="IBObjectContainer" key="IBDocument.Objects">
<object class="NSMutableArray" key="connectionRecords">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="841351856"/>
<reference key="destination" ref="664661524"/>
</object>
<int key="connectionID">4</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">viewController</string>
<reference key="source" ref="664661524"/>
<reference key="destination" ref="943309135"/>
</object>
<int key="connectionID">11</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">window</string>
<reference key="source" ref="664661524"/>
<reference key="destination" ref="117978783"/>
</object>
<int key="connectionID">14</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBObjectRecord">
<int key="objectID">0</int>
<reference key="object" ref="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="841351856"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">3</int>
<reference key="object" ref="664661524"/>
<reference key="parent" ref="0"/>
<string key="objectName">Pong App Delegate</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="427554174"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">10</int>
<reference key="object" ref="943309135"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">12</int>
<reference key="object" ref="117978783"/>
<reference key="parent" ref="0"/>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>-1.CustomClassName</string>
<string>-2.CustomClassName</string>
<string>10.CustomClassName</string>
<string>10.IBEditorWindowLastContentRect</string>
<string>10.IBPluginDependency</string>
<string>12.IBEditorWindowLastContentRect</string>
<string>12.IBPluginDependency</string>
<string>3.CustomClassName</string>
<string>3.IBPluginDependency</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>UIApplication</string>
<string>UIResponder</string>
<string>PongViewController</string>
<string>{{234, 376}, {320, 480}}</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>{{525, 346}, {320, 480}}</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>PongAppDelegate</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</object>
</object>
<object class="NSMutableDictionary" key="unlocalizedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="0"/>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<nil key="activeLocalization"/>
<object class="NSMutableDictionary" key="localizations">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="0"/>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<nil key="sourceID"/>
<int key="maxID">15</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">UIWindow</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBUserSource</string>
<string key="minorKey"/>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">PongAppDelegate</string>
<string key="superclassName">NSObject</string>
<object class="NSMutableDictionary" key="outlets">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>viewController</string>
<string>window</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>PongViewController</string>
<string>UIWindow</string>
</object>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>viewController</string>
<string>window</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBToOneOutletInfo">
<string key="name">viewController</string>
<string key="candidateClassName">PongViewController</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">window</string>
<string key="candidateClassName">UIWindow</string>
</object>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">Classes/PongAppDelegate.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">PongAppDelegate</string>
<string key="superclassName">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBUserSource</string>
<string key="minorKey"/>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">PongViewController</string>
<string key="superclassName">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">Classes/PongViewController.h</string>
</object>
</object>
</object>
<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSError.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIAccessibility.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UINibLoading.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="356479594">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIResponder.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIApplication</string>
<string key="superclassName">UIResponder</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIApplication.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIResponder</string>
<string key="superclassName">NSObject</string>
<reference key="sourceIdentifier" ref="356479594"/>
</object>
<object class="IBPartialClassDescription">
<string key="className">UISearchBar</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISearchBar.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UISearchDisplayController</string>
<string key="superclassName">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISearchDisplayController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UITextField.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIView</string>
<string key="superclassName">UIResponder</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UINavigationController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIPopoverController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISplitViewController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UITabBarController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<string key="superclassName">UIResponder</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIViewController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIWindow</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIWindow.h</string>
</object>
</object>
</object>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
<integer value="1024" key="NS.object.0"/>
</object>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string>
<integer value="3100" key="NS.object.0"/>
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<string key="IBDocument.LastKnownRelativeProjectPath">Pong.xcodeproj</string>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<string key="IBCocoaTouchPluginVersion">112</string>
</data>
</archive>

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>net.jeena.apps.pong</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSMainNibFile</key>
<string>MainWindow</string>
<key>UIStatusBarHidden</key>
<true/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
</array>
</dict>
</plist>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,302 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 45;
objects = {
/* Begin PBXBuildFile section */
1D3623260D0F684500981E51 /* PongAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* PongAppDelegate.m */; };
1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; };
1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; };
1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; };
1F369FF813233C1E004E7A99 /* lost.wav in Resources */ = {isa = PBXBuildFile; fileRef = 1F369FF513233C1E004E7A99 /* lost.wav */; };
1F369FF913233C1E004E7A99 /* ping.wav in Resources */ = {isa = PBXBuildFile; fileRef = 1F369FF613233C1E004E7A99 /* ping.wav */; };
1F369FFA13233C1E004E7A99 /* pong.wav in Resources */ = {isa = PBXBuildFile; fileRef = 1F369FF713233C1E004E7A99 /* pong.wav */; };
1F36A00713233CCC004E7A99 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F36A00613233CCC004E7A99 /* AVFoundation.framework */; };
1FBEBF481319FC56006D5497 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FBEBF471319FC56006D5497 /* CFNetwork.framework */; };
1FBEBF4D1319FCDE006D5497 /* AsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FBEBF4C1319FCDE006D5497 /* AsyncSocket.m */; };
1FBEBFEF131A97F8006D5497 /* GGSNetwork.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FBEBFEE131A97F8006D5497 /* GGSNetwork.m */; };
288765A50DF7441C002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765A40DF7441C002DB57D /* CoreGraphics.framework */; };
2899E5220DE3E06400AC0155 /* PongViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2899E5210DE3E06400AC0155 /* PongViewController.xib */; };
28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28AD733E0D9D9553002E5188 /* MainWindow.xib */; };
28D7ACF80DDB3853001CB0EB /* PongViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 28D7ACF70DDB3853001CB0EB /* PongViewController.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
1D3623240D0F684500981E51 /* PongAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PongAppDelegate.h; sourceTree = "<group>"; };
1D3623250D0F684500981E51 /* PongAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PongAppDelegate.m; sourceTree = "<group>"; };
1D6058910D05DD3D006BFB54 /* Pong.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Pong.app; sourceTree = BUILT_PRODUCTS_DIR; };
1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
1F369FF513233C1E004E7A99 /* lost.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = lost.wav; sourceTree = "<group>"; };
1F369FF613233C1E004E7A99 /* ping.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = ping.wav; sourceTree = "<group>"; };
1F369FF713233C1E004E7A99 /* pong.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = pong.wav; sourceTree = "<group>"; };
1F36A00613233CCC004E7A99 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
1FBEBF471319FC56006D5497 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
1FBEBF4B1319FCDE006D5497 /* AsyncSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AsyncSocket.h; sourceTree = "<group>"; };
1FBEBF4C1319FCDE006D5497 /* AsyncSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AsyncSocket.m; sourceTree = "<group>"; };
1FBEBFED131A97F8006D5497 /* GGSNetwork.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GGSNetwork.h; sourceTree = "<group>"; };
1FBEBFEE131A97F8006D5497 /* GGSNetwork.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GGSNetwork.m; sourceTree = "<group>"; };
1FBEC030131AF83B006D5497 /* GGSDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GGSDelegate.h; sourceTree = "<group>"; };
288765A40DF7441C002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
2899E5210DE3E06400AC0155 /* PongViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PongViewController.xib; sourceTree = "<group>"; };
28AD733E0D9D9553002E5188 /* MainWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = "<group>"; };
28D7ACF60DDB3853001CB0EB /* PongViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PongViewController.h; sourceTree = "<group>"; };
28D7ACF70DDB3853001CB0EB /* PongViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PongViewController.m; sourceTree = "<group>"; };
29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
32CA4F630368D1EE00C91783 /* Pong_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Pong_Prefix.pch; sourceTree = "<group>"; };
8D1107310486CEB800E47090 /* Pong-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Pong-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
1D60588F0D05DD3D006BFB54 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */,
1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */,
288765A50DF7441C002DB57D /* CoreGraphics.framework in Frameworks */,
1FBEBF481319FC56006D5497 /* CFNetwork.framework in Frameworks */,
1F36A00713233CCC004E7A99 /* AVFoundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
080E96DDFE201D6D7F000001 /* Classes */ = {
isa = PBXGroup;
children = (
1FBEBF4B1319FCDE006D5497 /* AsyncSocket.h */,
1FBEBF4C1319FCDE006D5497 /* AsyncSocket.m */,
1FA0569C12F0B528003F1373 /* Views */,
1D3623240D0F684500981E51 /* PongAppDelegate.h */,
1D3623250D0F684500981E51 /* PongAppDelegate.m */,
28D7ACF60DDB3853001CB0EB /* PongViewController.h */,
28D7ACF70DDB3853001CB0EB /* PongViewController.m */,
1FBEC030131AF83B006D5497 /* GGSDelegate.h */,
1FBEBFED131A97F8006D5497 /* GGSNetwork.h */,
1FBEBFEE131A97F8006D5497 /* GGSNetwork.m */,
);
path = Classes;
sourceTree = "<group>";
};
19C28FACFE9D520D11CA2CBB /* Products */ = {
isa = PBXGroup;
children = (
1D6058910D05DD3D006BFB54 /* Pong.app */,
);
name = Products;
sourceTree = "<group>";
};
1FA0569C12F0B528003F1373 /* Views */ = {
isa = PBXGroup;
children = (
);
name = Views;
sourceTree = "<group>";
};
29B97314FDCFA39411CA2CEA /* CustomTemplate */ = {
isa = PBXGroup;
children = (
080E96DDFE201D6D7F000001 /* Classes */,
29B97315FDCFA39411CA2CEA /* Other Sources */,
29B97317FDCFA39411CA2CEA /* Resources */,
29B97323FDCFA39411CA2CEA /* Frameworks */,
19C28FACFE9D520D11CA2CBB /* Products */,
);
name = CustomTemplate;
sourceTree = "<group>";
};
29B97315FDCFA39411CA2CEA /* Other Sources */ = {
isa = PBXGroup;
children = (
32CA4F630368D1EE00C91783 /* Pong_Prefix.pch */,
29B97316FDCFA39411CA2CEA /* main.m */,
);
name = "Other Sources";
sourceTree = "<group>";
};
29B97317FDCFA39411CA2CEA /* Resources */ = {
isa = PBXGroup;
children = (
2899E5210DE3E06400AC0155 /* PongViewController.xib */,
28AD733E0D9D9553002E5188 /* MainWindow.xib */,
1F369FF513233C1E004E7A99 /* lost.wav */,
1F369FF613233C1E004E7A99 /* ping.wav */,
1F369FF713233C1E004E7A99 /* pong.wav */,
8D1107310486CEB800E47090 /* Pong-Info.plist */,
);
name = Resources;
sourceTree = "<group>";
};
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
isa = PBXGroup;
children = (
1DF5F4DF0D08C38300B7A737 /* UIKit.framework */,
1D30AB110D05D00D00671497 /* Foundation.framework */,
288765A40DF7441C002DB57D /* CoreGraphics.framework */,
1FBEBF471319FC56006D5497 /* CFNetwork.framework */,
1F36A00613233CCC004E7A99 /* AVFoundation.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
1D6058900D05DD3D006BFB54 /* Pong */ = {
isa = PBXNativeTarget;
buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "Pong" */;
buildPhases = (
1D60588D0D05DD3D006BFB54 /* Resources */,
1D60588E0D05DD3D006BFB54 /* Sources */,
1D60588F0D05DD3D006BFB54 /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Pong;
productName = Pong;
productReference = 1D6058910D05DD3D006BFB54 /* Pong.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
29B97313FDCFA39411CA2CEA /* Project object */ = {
isa = PBXProject;
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Pong" */;
compatibilityVersion = "Xcode 3.1";
developmentRegion = English;
hasScannedForEncodings = 1;
knownRegions = (
English,
Japanese,
French,
German,
);
mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */;
projectDirPath = "";
projectRoot = "";
targets = (
1D6058900D05DD3D006BFB54 /* Pong */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
1D60588D0D05DD3D006BFB54 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */,
2899E5220DE3E06400AC0155 /* PongViewController.xib in Resources */,
1F369FF813233C1E004E7A99 /* lost.wav in Resources */,
1F369FF913233C1E004E7A99 /* ping.wav in Resources */,
1F369FFA13233C1E004E7A99 /* pong.wav in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
1D60588E0D05DD3D006BFB54 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1D60589B0D05DD56006BFB54 /* main.m in Sources */,
1D3623260D0F684500981E51 /* PongAppDelegate.m in Sources */,
28D7ACF80DDB3853001CB0EB /* PongViewController.m in Sources */,
1FBEBF4D1319FCDE006D5497 /* AsyncSocket.m in Sources */,
1FBEBFEF131A97F8006D5497 /* GGSNetwork.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
1D6058940D05DD3E006BFB54 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Pong_Prefix.pch;
INFOPLIST_FILE = "Pong-Info.plist";
PRODUCT_NAME = Pong;
};
name = Debug;
};
1D6058950D05DD3E006BFB54 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Pong_Prefix.pch;
INFOPLIST_FILE = "Pong-Info.plist";
PRODUCT_NAME = Pong;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
C01FCF4F08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Richard Pannek (G62Q88N36M)";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 3.0;
PREBINDING = NO;
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "6A9A419F-E593-49FC-98DE-2B027A0982C3";
SDKROOT = iphoneos;
};
name = Debug;
};
C01FCF5008A954540054247B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
PREBINDING = NO;
SDKROOT = iphoneos;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "Pong" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1D6058940D05DD3E006BFB54 /* Debug */,
1D6058950D05DD3E006BFB54 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Pong" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C01FCF4F08A954540054247B /* Debug */,
C01FCF5008A954540054247B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Pong.xcodeproj">
</FileRef>
</Workspace>

View file

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1D6058900D05DD3D006BFB54"
BuildableName = "Pong.app"
BlueprintName = "Pong"
ReferencedContainer = "container:Pong.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
</Testables>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
displayScaleIsEnabled = "NO"
displayScale = "1.00"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug">
<BuildableProductRunnable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1D6058900D05DD3D006BFB54"
BuildableName = "Pong.app"
BlueprintName = "Pong"
ReferencedContainer = "container:Pong.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
displayScaleIsEnabled = "NO"
displayScale = "1.00"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release">
<BuildableProductRunnable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1D6058900D05DD3D006BFB54"
BuildableName = "Pong.app"
BlueprintName = "Pong"
ReferencedContainer = "container:Pong.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Pong.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>1D6058900D05DD3D006BFB54</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View file

@ -0,0 +1,635 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
<data>
<int key="IBDocument.SystemTarget">1056</int>
<string key="IBDocument.SystemVersion">10J567</string>
<string key="IBDocument.InterfaceBuilderVersion">823</string>
<string key="IBDocument.AppKitVersion">1038.35</string>
<string key="IBDocument.HIToolboxVersion">462.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">132</string>
</object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
<integer value="6"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</object>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys" id="0">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBProxyObject" id="372490531">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBProxyObject" id="843779117">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBUIView" id="774585933">
<reference key="NSNextResponder"/>
<int key="NSvFlags">301</int>
<object class="NSMutableArray" key="NSSubviews">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBUIView" id="228194198">
<reference key="NSNextResponder" ref="774585933"/>
<int key="NSvFlags">260</int>
<string key="NSFrame">{{20, 99}, {20, 90}}</string>
<reference key="NSSuperview" ref="774585933"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
<object class="NSColorSpace" key="NSCustomColorSpace" id="369936712">
<int key="NSID">2</int>
</object>
</object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBUIView" id="915866746">
<reference key="NSNextResponder" ref="774585933"/>
<int key="NSvFlags">256</int>
<string key="NSFrame">{{221, 135}, {20, 20}}</string>
<reference key="NSSuperview" ref="774585933"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
<reference key="NSCustomColorSpace" ref="369936712"/>
</object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBUIView" id="504982683">
<reference key="NSNextResponder" ref="774585933"/>
<int key="NSvFlags">260</int>
<string key="NSFrame">{{440, 99}, {20, 90}}</string>
<reference key="NSSuperview" ref="774585933"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
<reference key="NSCustomColorSpace" ref="369936712"/>
</object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBUILabel" id="21349180">
<reference key="NSNextResponder" ref="774585933"/>
<int key="NSvFlags">301</int>
<string key="NSFrame">{{129, 227}, {203, 43}}</string>
<reference key="NSSuperview" ref="774585933"/>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
<int key="IBUIContentMode">7</int>
<bool key="IBUIUserInteractionEnabled">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<string key="IBUIText">Tap to begin</string>
<object class="NSFont" key="IBUIFont">
<string key="NSName">Helvetica</string>
<double key="NSSize">36</double>
<int key="NSfFlags">16</int>
</object>
<object class="NSColor" key="IBUITextColor" id="492430979">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MCAwIDAAA</bytes>
</object>
<object class="NSColor" key="IBUIHighlightedColor" id="533962776">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
<object class="NSColor" key="IBUIShadowColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
</object>
<string key="IBUIShadowOffset">{2, 1}</string>
<int key="IBUIBaselineAdjustment">1</int>
<float key="IBUIMinimumFontSize">10</float>
</object>
<object class="IBUILabel" id="532396815">
<reference key="NSNextResponder" ref="774585933"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{86, 20}, {42, 21}}</string>
<reference key="NSSuperview" ref="774585933"/>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
<int key="IBUIContentMode">7</int>
<bool key="IBUIUserInteractionEnabled">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<string key="IBUIText">0</string>
<reference key="IBUITextColor" ref="492430979"/>
<reference key="IBUIHighlightedColor" ref="533962776"/>
<int key="IBUIBaselineAdjustment">1</int>
<float key="IBUIMinimumFontSize">10</float>
<int key="IBUITextAlignment">2</int>
</object>
<object class="IBUILabel" id="738971272">
<reference key="NSNextResponder" ref="774585933"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{340, 20}, {42, 21}}</string>
<reference key="NSSuperview" ref="774585933"/>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
<int key="IBUIContentMode">7</int>
<bool key="IBUIUserInteractionEnabled">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<string key="IBUIText">0</string>
<reference key="IBUITextColor" ref="492430979"/>
<reference key="IBUIHighlightedColor" ref="533962776"/>
<int key="IBUIBaselineAdjustment">1</int>
<float key="IBUIMinimumFontSize">10</float>
</object>
</object>
<string key="NSFrameSize">{480, 300}</string>
<reference key="NSSuperview"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">2</int>
<bytes key="NSRGB">MCAwLjg5NDExNzcxMyAwLjA2Mjc0NTEwMTc1AA</bytes>
</object>
<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
<bool key="IBUIMultipleTouchEnabled">YES</bool>
<object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
<int key="interfaceOrientation">3</int>
</object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
</object>
<object class="IBObjectContainer" key="IBDocument.Objects">
<object class="NSMutableArray" key="connectionRecords">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">view</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="774585933"/>
</object>
<int key="connectionID">29</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">tapToBegin</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="21349180"/>
</object>
<int key="connectionID">30</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">pointsP2</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="738971272"/>
</object>
<int key="connectionID">31</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">pointsP1</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="532396815"/>
</object>
<int key="connectionID">32</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">player2View</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="504982683"/>
</object>
<int key="connectionID">33</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">player1View</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="228194198"/>
</object>
<int key="connectionID">34</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">ballView</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="915866746"/>
</object>
<int key="connectionID">35</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBObjectRecord">
<int key="objectID">0</int>
<reference key="object" ref="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="372490531"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="843779117"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">6</int>
<reference key="object" ref="774585933"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="915866746"/>
<reference ref="228194198"/>
<reference ref="504982683"/>
<reference ref="532396815"/>
<reference ref="738971272"/>
<reference ref="21349180"/>
</object>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">8</int>
<reference key="object" ref="228194198"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<reference key="parent" ref="774585933"/>
<string key="objectName">Player1</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">9</int>
<reference key="object" ref="504982683"/>
<reference key="parent" ref="774585933"/>
<string key="objectName">Player2</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">10</int>
<reference key="object" ref="915866746"/>
<reference key="parent" ref="774585933"/>
<string key="objectName">Ball</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">20</int>
<reference key="object" ref="21349180"/>
<reference key="parent" ref="774585933"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">22</int>
<reference key="object" ref="532396815"/>
<reference key="parent" ref="774585933"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">23</int>
<reference key="object" ref="738971272"/>
<reference key="parent" ref="774585933"/>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>-1.CustomClassName</string>
<string>-2.CustomClassName</string>
<string>10.IBPluginDependency</string>
<string>10.IBViewBoundsToFrameTransform</string>
<string>20.IBPluginDependency</string>
<string>20.IBViewBoundsToFrameTransform</string>
<string>22.IBPluginDependency</string>
<string>22.IBViewBoundsToFrameTransform</string>
<string>23.IBPluginDependency</string>
<string>23.IBViewBoundsToFrameTransform</string>
<string>6.IBEditorWindowLastContentRect</string>
<string>6.IBPluginDependency</string>
<string>8.IBPluginDependency</string>
<string>8.IBViewBoundsToFrameTransform</string>
<string>9.IBPluginDependency</string>
<string>9.IBViewBoundsToFrameTransform</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>PongViewController</string>
<string>UIResponder</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<object class="NSAffineTransform">
<bytes key="NSTransformStruct">P4AAAL+AAABDCgAAw2wAAA</bytes>
</object>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<object class="NSAffineTransform">
<bytes key="NSTransformStruct">P4AAAL+AAABDAQAAwyQAAA</bytes>
</object>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<object class="NSAffineTransform">
<bytes key="NSTransformStruct">AUKsAABBoAAAA</bytes>
</object>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<object class="NSAffineTransform">
<bytes key="NSTransformStruct">P4AAAL+AAABCtgAAwjAAAA</bytes>
</object>
<string>{{546, 448}, {480, 300}}</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<object class="NSAffineTransform">
<bytes key="NSTransformStruct">P4AAAL+AAABBQAAAw4iAAA</bytes>
</object>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<object class="NSAffineTransform">
<bytes key="NSTransformStruct">P4AAAL+AAABDOQAAw7GAAA</bytes>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="unlocalizedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="0"/>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<nil key="activeLocalization"/>
<object class="NSMutableDictionary" key="localizations">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="0"/>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<nil key="sourceID"/>
<int key="maxID">35</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">PongViewController</string>
<string key="superclassName">UIViewController</string>
<object class="NSMutableDictionary" key="outlets">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>ballView</string>
<string>player1View</string>
<string>player2View</string>
<string>pointsP1</string>
<string>pointsP2</string>
<string>tapToBegin</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>UIView</string>
<string>UIView</string>
<string>UIView</string>
<string>UILabel</string>
<string>UILabel</string>
<string>UIView</string>
</object>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>ballView</string>
<string>player1View</string>
<string>player2View</string>
<string>pointsP1</string>
<string>pointsP2</string>
<string>tapToBegin</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBToOneOutletInfo">
<string key="name">ballView</string>
<string key="candidateClassName">UIView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">player1View</string>
<string key="candidateClassName">UIView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">player2View</string>
<string key="candidateClassName">UIView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">pointsP1</string>
<string key="candidateClassName">UILabel</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">pointsP2</string>
<string key="candidateClassName">UILabel</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">tapToBegin</string>
<string key="candidateClassName">UIView</string>
</object>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">Classes/PongViewController.h</string>
</object>
</object>
</object>
<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSError.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIAccessibility.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UINibLoading.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="844179110">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIResponder.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UILabel</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UILabel.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIResponder</string>
<string key="superclassName">NSObject</string>
<reference key="sourceIdentifier" ref="844179110"/>
</object>
<object class="IBPartialClassDescription">
<string key="className">UISearchBar</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISearchBar.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UISearchDisplayController</string>
<string key="superclassName">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISearchDisplayController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIPrintFormatter.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UITextField.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIView</string>
<string key="superclassName">UIResponder</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UINavigationController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIPopoverController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISplitViewController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UITabBarController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<string key="superclassName">UIResponder</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIViewController.h</string>
</object>
</object>
</object>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
<integer value="1056" key="NS.object.0"/>
</object>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string>
<integer value="3100" key="NS.object.0"/>
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<string key="IBDocument.LastKnownRelativeProjectPath">Pong.xcodeproj</string>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<string key="IBCocoaTouchPluginVersion">132</string>
</data>
</archive>

View file

@ -0,0 +1,8 @@
//
// Prefix header for all source files of the 'Pong' target in the 'Pong' project
//
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#endif

BIN
games/Pong/lost.wav Normal file

Binary file not shown.

17
games/Pong/main.m Normal file
View file

@ -0,0 +1,17 @@
//
// main.m
// Pong
//
// Created by Jeena on 26.01.11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}

BIN
games/Pong/ping.wav Normal file

Binary file not shown.

BIN
games/Pong/pong.wav Normal file

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,121 @@
-module(ggs_network).
-export([connect/0,append_key_value_strings_to_dict/2,key_value_string_to_list/1]).
-export([read/2, send_command/3]).
%For quickcheck
-export([receive_content/1,receive_data/3]).
connect() ->
%{ok,Socket} = gen_tcp:connect("ggs.jeena.net", 9000,[{active, false}]),
{ok,Socket} = gen_tcp:connect("localhost", 9000,[{active, false}]).
read(Socket, Ref) ->
Content = receive_content(Socket),
Headers = extract_headers(Content),
ContentSize = dict:fetch("Content-Size", Headers),
ContentSizeI = list_to_integer(lists:nth(1, ContentSize)),
Data = receive_data(Socket, ContentSizeI, ""),
%io:format("Headers: ~s~n", [Content]),
%io:format("Data: ~s~n", [Data]),
received_command(Headers, Data, Ref).
receive_content(Socket) ->
receive_content_(0, "", Socket).
receive_content_(Amount, Headers, Socket) ->
{ok, Char1} = gen_tcp:recv(Socket, 1),
case Char1 of
"\n" -> case Amount of
1 -> Headers;
_ -> receive_content_(Amount + 1,
Headers ++ Char1,
Socket)
end;
_ -> receive_content_(0, Headers ++ Char1, Socket)
end.
receive_data(_, 0, Headers) ->
Headers;
receive_data(Socket, ContentSize, Headers) ->
{ok, Char} = gen_tcp:recv(Socket, 1),
receive_data(Socket, ContentSize - 1, Headers ++ Char).
received_command(Headers, Data, Ref) ->
{ok, CommandList} = dict:find("Client-Command", Headers),
Command = lists:nth(1, CommandList),
case Command of
"hello" ->
pong_bot:set_game_token(Data, Ref),
send_command("ready", "", Ref);
"defined" ->
ok;
Command ->
pong_bot:ggsNetworkReceivedCommandWithArgs(Command, Data, Ref)
end.
make_message(ServerOrGame, Command, Args, Ref) ->
GameToken = pong_bot:get_game_token(Ref),
StrGameToken = string:concat("Token: ", GameToken),
StrGameTokenln = string:concat(StrGameToken, "\n"),
StrCommand = string:concat("-Command: ", Command),
StrCommandln = string:concat(StrCommand, "\n"),
StrFullCommand = string:concat(ServerOrGame, StrCommandln),
StrContentLength = string:concat("Content-Length: ", integer_to_list(length(Args))),
StrContentLengthln = string:concat(StrContentLength, "\n\n"),
StrTokenCommand = string:concat(StrGameTokenln, StrFullCommand),
Message = string:concat(StrTokenCommand, StrContentLengthln),
MessageWithArgs = string:concat(Message, list_concat(Args,[])),
MessageWithArgs.
send_command(Command, Args, Ref) ->
write(make_message("Game", Command, Args, Ref), Ref).
write(Message, Ref) ->
Socket = gen_server:call({global, {pong_bot, Ref}}, socket),
gen_tcp:send(Socket, Message).
list_concat([],Ret) ->
Ret;
list_concat([E|ES],Ret) ->
NewRet = string:concat(Ret,E),
list_concat(ES,NewRet).
%%%Packet parsing.%%%
extract_headers(Source) ->
HeaderList = string:tokens(Source, "\n"),
key_value_strings_to_dict(HeaderList).
%%%Low-level internals.%%%
%%["K1: V1","K2: V2","KN: VN" ...] -> Dict
key_value_strings_to_dict(Strings) ->
Dict = dict:new(),
append_key_value_strings_to_dict(Strings,Dict).
%%["K1: V1","K2: V2","KN: VN" ...], Dict -> NewDict
append_key_value_strings_to_dict([Str|Strings],Dict) ->
KeyValueList = key_value_string_to_list(Str),
case length(KeyValueList) of
2 ->
NewDict = append_string_pair_to_dict(Dict,lists:nth(1,KeyValueList),lists:nth(2,KeyValueList)),
append_key_value_strings_to_dict(Strings,NewDict);
_ ->
append_key_value_strings_to_dict(Strings,Dict)
end;
append_key_value_strings_to_dict([],Dict) ->
Dict.
%%"Hello: "World!" -> ["Hello","World!"]
key_value_string_to_list(KeyValueString) ->
string:tokens(KeyValueString, ": ").
%%Append a key str1 and a value str2 to the dict Dict
append_string_pair_to_dict(Dict, Str1, Str2) ->
dict:append(Str1, Str2, Dict).

View file

@ -0,0 +1,86 @@
-module(ggs_network_eqc_test).
-include_lib("../../lib/eqc/include/eqc.hrl").
-compile(export_all).
start() ->
{ok, ListenSocket} = listen(),
eqc:quickcheck(prop_connect()),
gen_tcp:close(ListenSocket),
{ok, ListenSocket2} = listen(),
eqc:quickcheck(prop_receive_content(ListenSocket2)),
%gen_tcp:close(ListenSocket2),
%timer:sleep(100),
%{ok, ListenSocket} = listen(),
eqc:quickcheck(prop_receive_data(ListenSocket2)).
prop_connect() ->
{Atom, Socket} = ggs_network:connect(),
gen_tcp:close(Socket),
eqc:equals(Atom, ok).
%% ?String++\n\n -> ok
prop_receive_content(ListenSocket) ->
G = fun(N) -> String = integer_to_list(N) ++"\n\n",
accept_run_compare(String,ListenSocket, fun(X) -> ggs_network:receive_content(X) end, "\n") end,
?FORALL(NaturalNumber, nat(), G(NaturalNumber)).
%% old(String) == new(String)
prop_receive_data(ListenSocket) ->
G = fun(N) -> String = integer_to_list(N),
Length = length(String),
accept_run_compare(String,ListenSocket, fun(S) -> ggs_network:receive_data(S,Length,"") end, "") end,
?FORALL(NaturalNumber, nat(), G(NaturalNumber)).
%% Helpers
accept_run_compare(Arg, ListenSocket, Fun, Newline) ->
spawn(fun() -> {ok, AcceptSocket} = accept(ListenSocket),
gen_tcp:send(AcceptSocket, Arg),
gen_tcp:close(AcceptSocket) end),
{ok, ConnectSocket} = ggs_network:connect(),
Pid = spawn(fun() -> C = Fun(ConnectSocket),
gen_tcp:close(ConnectSocket),
receive
{From, getcontent} -> From!{C} end end ),
Pid!{self(), getcontent},
receive
{C} -> eqc:equals(C++Newline,Arg) end.
listen() ->
listen(9000).
listen(Port) ->
case gen_tcp:listen(Port, [binary, {packet, 0}, {active, false}]) of
{ok, LSock} ->
{ok, LSock};
{error, Reason} ->
io:format("Creation of listen socket failed: ~s~n", [Reason])
end.
recv(AcceptSocket) ->
gen_tcp:recv(AcceptSocket, 0).
accept(ListenSocket) ->
case gen_tcp:accept(ListenSocket) of
{ok, Socket} -> {ok, Socket};
{error, Reason} -> io:format("Error accepting listen socket~s~n", [Reason]);
_ -> io:format("Something bad happened with accept/1~n")
end.
%% new(length) == old(length) or
%% new(length) == old(length) + 1
prop_setItem() ->
ggs_db:init(),
F = (fun(T,N,K,V) -> ggs_db:setItem(T,N,K,V), ggs_db:length(T,N) end),
?FORALL({T,N,K,V},{bitstring(),bitstring(),bitstring(),bitstring()},
(ggs_db:length(T,N) + 1 == F(T,N,K,V)) or
(ggs_db:length(T,N) == F(T,N,K,V))).

View file

@ -0,0 +1,255 @@
-module(pong_bot).
-behaviour(gen_server).
-export([start/1, start_link/0]).
-export([init/1, handle_call/3, handle_cast/2]).
-export([ggsNetworkReceivedCommandWithArgs/3,set_game_token/2,get_game_token/1]).
-export([view/1, peek_socket/1]).
start(0) ->
ok;
start(N) ->
start_link(),
timer:sleep(50),
start(N - 1).
start_link() ->
Ref = make_ref(),
gen_server:start_link({global, {pong_bot, Ref}}, pong_bot, [], []),
Socket = peek_socket(Ref),
spawn(fun() -> communication_loop(Socket, Ref) end),
spawn(fun() -> game_loop(Ref) end ).
communication_loop(Socket, Ref) ->
ggs_network:read(Socket, Ref),
communication_loop(Socket, Ref).
peek_socket(Ref) ->
gen_server:call({global, {pong_bot, Ref}}, socket).
init(_Args) ->
Player1 = new_pos(),
Player2 = new_pos(),
Ball = new_pos(),
Paused = true,
Start = false,
{ok, Socket} = ggs_network:connect(), %Localhost is set internally inside ggs_network.
State1 = dict:new(),
State2 = dict:store(player1, Player1, State1),
State3 = dict:store(player2, Player2, State2),
State4 = dict:store(ball, Ball, State3),
State5 = dict:store(paused, Paused, State4),
State6 = dict:store(start, Start, State5),
State = dict:store(socket, Socket, State6),
{ok, State}.
new_pos() ->
{0, 0}.
ggsNetworkReceivedCommandWithArgs(Command, Args, Ref) ->
case Command of
"welcome" ->
welcome(Args, Ref);
"ball" ->
ball(Args, Ref);
"player1_y" ->
player1_y(Args, Ref);
"player2_y" ->
player2_y(Args, Ref);
"game" ->
game(Args, Ref);
"player1_points" ->
%io:format("Player1 win~n"),
new_round(Ref);
"player2_points" ->
%io:format("Player2 win~n"),
new_round(Ref);
_ -> ok
end.
welcome(Who_am_I, Ref) ->
gen_server:cast({global, {pong_bot, Ref}}, {me, Who_am_I}).
%case Who_am_I of
% "1" ->
% Me = gen_server:call({global, {pong_bot, Ref}}, player1),
% gen_server:cast({global, {pong_bot, Ref}}, {me, Me});
% "2" ->
% Me = gen_server:call({global, {pong_bot, Ref}}, player2),
% gen_server:cast({global, {pong_bot, Ref}}, {me, Me})
%end.
game_loop(Ref) ->
timer:sleep(300),
gameTick(Ref),
game_loop(Ref).
gameTick(Ref) ->
GamePaused = gen_server:call({global, {pong_bot, Ref}}, paused),
SendStart = gen_server:call({global, {pong_bot, Ref}}, start),
case GamePaused of
true ->
case SendStart of
false ->
ggs_network:send_command("start", "", Ref),
gen_server:cast({global, {pong_bot, Ref}}, {start, true});
true ->
ok
end;
false ->
Ball = gen_server:call({global, {pong_bot, Ref}}, ball),
{_, BallY} = Ball,
Me = gen_server:call({global, {pong_bot, Ref}}, me),
case Me of
"1" ->
PlayerMe = gen_server:call({global, {pong_bot, Ref}}, player1);
"2" ->
PlayerMe = gen_server:call({global, {pong_bot, Ref}}, player2)
end,
{_, MeY} = PlayerMe,
case ((BallY - MeY) < 0) of
true ->
ggs_network:send_command("up", "", Ref);
%io:format("Player down sent to server~n"),
%io:format("Ball: ~B~n", [BallY]),
%io:format("Player: ~B~n", [MeY]);
_ -> case ((BallY - MeY) > 0) of
true ->
ggs_network:send_command("down", "", Ref);
%io:format("Player up sent to server~n"),
%io:format("Ball: ~B~n", [BallY]),
%io:format("Player: ~B~n", [MeY]);
_ -> ok
end
end
end.
ball(Pos_s, Ref) ->
PosList = string:tokens(Pos_s, ","),
XStr = lists:nth(1,PosList),
YStr = lists:nth(2,PosList),
X = list_to_integer(XStr),
Y = list_to_integer(YStr),
Pos = {X, Y},
gen_server:cast({global, {pong_bot, Ref}}, {ball, Pos}).
player1_y(YStr, Ref) ->
Y = list_to_integer(YStr),
%io:format("Player1_y~n~n~n~n"),
gen_server:cast({global, {pong_bot, Ref}}, {player1_y, Y}).
player2_y(YStr, Ref) ->
Y = list_to_integer(YStr),
%io:format("Player2_y~n~n~n~n"),
gen_server:cast({global, {pong_bot, Ref}}, {player2_y, Y}).
game(WaitOrStart, Ref) ->
case WaitOrStart of
"wait" ->
ok;
_ ->
gen_server:cast({global, {pong_bot, Ref}}, {paused, false})
end.
new_round(Ref) ->
Paused = true,
Start = false,
gen_server:cast({global, {pong_bot, Ref}}, {new_round, Paused, Start}).
set_game_token(GameToken, Ref) ->
gen_server:cast({global, {pong_bot, Ref}}, {game_token, GameToken}).
get_game_token(Ref) ->
gen_server:call({global, {pong_bot, Ref}}, game_token).
view(Ref) ->
gen_server:call({global, {pong_bot, Ref}}, game_token).
handle_call(player1, _From, State) ->
Player1 = dict:fetch(player1, State),
{reply, Player1, State};
handle_call(player2, _From, State) ->
Player2 = dict:fetch(player2, State),
{reply, Player2, State};
handle_call(player1_y, _From, State) ->
{_,Y} = dict:fetch(player1, State),
{reply, Y, State};
handle_call(player2_y, _From, State) ->
{_,Y} = dict:fetch(player2, State),
{reply, Y, State};
handle_call(ball, _From, State) ->
Ball = dict:fetch(ball, State),
{reply, Ball, State};
handle_call(me, _From, State) ->
Me = dict:fetch(me, State),
{reply, Me, State};
handle_call(game_token, _From, State) ->
GameToken = dict:fetch(game_token, State),
{reply, GameToken, State};
handle_call(view, _From, State) ->
{reply, State, State};
handle_call(socket, _From, State) ->
Socket = dict:fetch(socket, State),
{reply, Socket, State};
handle_call(paused, _From, State) ->
Paused = dict:fetch(paused, State),
{reply, Paused, State};
handle_call(start, _From, State) ->
Start = dict:fetch(start, State),
{reply, Start, State}.
handle_cast({game_token, GameToken}, State) ->
NewState = dict:store(game_token, GameToken, State),
{noreply, NewState};
handle_cast({me, Me}, State) ->
NewState = dict:store(me, Me, State),
{noreply, NewState};
handle_cast({ball, Pos}, State) ->
NewState = dict:store(ball, Pos, State),
{noreply, NewState};
handle_cast({player1_y, Y}, State) ->
{OldX, _} = dict:fetch(player1, State),
NewPlayer1 = {OldX, Y},
NewState = dict:store(player1, NewPlayer1, State),
{noreply, NewState};
handle_cast({player2_y, Y}, State) ->
{OldX, _} = dict:fetch(player2, State),
NewPlayer2 = {OldX, Y},
NewState = dict:store(player2, NewPlayer2, State),
{noreply, NewState};
handle_cast({paused, Paused}, State) ->
NewState = dict:store(paused, Paused, State),
{noreply, NewState};
handle_cast({new_round, Paused, Start}, State) ->
State1 = dict:store(paused, Paused, State),
NewState = dict:store(start, Start, State1),
{noreply, NewState};
handle_cast({start, Start}, State) ->
NewState = dict:store(start, Start, State),
{noreply, NewState}.

View file

@ -0,0 +1,62 @@
-module(ggs_network_eqc_test).
-include_lib("../../lib/eqc/include/eqc.hrl").
-compile(export_all).
start() ->
eqc:quickcheck(prop_connect()).
prop_connect() ->
{Atom, Value} = ggs_network:connect(),
eqc:equals(Atom, ok).
%% new(length) == old(length) or
%% new(length) == old(length) + 1
prop_setItem() ->
ggs_db:init(),
F = (fun(T,N,K,V) -> ggs_db:setItem(T,N,K,V), ggs_db:length(T,N) end),
?FORALL({T,N,K,V},{bitstring(),bitstring(),bitstring(),bitstring()},
(ggs_db:length(T,N) + 1 == F(T,N,K,V)) or
(ggs_db:length(T,N) == F(T,N,K,V))).
%% new(length) >= 0 and
%% old(length) == new(length) or
%% old(length) - 1 == new(length)
prop_removeItem() ->
ggs_db:init(),
F = (fun(T,N,K) -> ggs_db:removeItem(T,N,K), ggs_db:length(T,N) end),
G = (fun(A,B) -> ((A == B) or (A == B + 1)) and (B >= 0) end),
?FORALL({T,N,K},{bitstring(),bitstring(),bitstring()},
G(ggs_db:length(T,N), F(T,N,K))).
%% clear(X) -> (length(X,?) == 0)
prop_clear() ->
ggs_db:init(),
F = (fun(T,N) -> ggs_db:clear(T), ggs_db:length(T,N) end),
?FORALL({T,N},{bitstring(),bitstring()},
F(T,N) == 0).
%% ? -> length(?,?) >= 0
prop_length() ->
ggs_db:init(),
F = fun(T,N,K,V) -> ggs_db:setItem(T,N,K,V), ggs_db:length(T,N) end,
G = fun(T,N,K) -> ggs_db:removeItem(T,N,K), ggs_db:length(T,N) end,
?FORALL({{T,N,K,V},{T2,N2,K2}},
{{bitstring(),bitstring(),bitstring(),bitstring()},
{bitstring(),bitstring(),bitstring()}},
(((F(T,N,K,V) >= 0) and (G(T2,N2,K2) >= 0)))).
%% key(X,Y,length(X,Y)) -> Exists
prop_key() ->
ggs_db:init(),
F = fun(T,N) -> case ggs_db:length(T,N) of 0 -> true; X ->
case ggs_db:key(T,N,X) of _ -> true end end end,
?FORALL({T,N},{bitstring(),bitstring()},
F(T,N)).

View file

@ -0,0 +1,90 @@
-module(ggs_network_eqc_test).
-include_lib("../../lib/eqc/include/eqc.hrl").
-compile(export_all).
start() ->
{ok, ListenSocket} = listen(),
register(listen, ListenSocket),
Pid = spawn(fun() -> {ok, AcceptSocket} = accept(ListenSocket),
receive {From, accept} -> From!{AcceptSocket} end end),
{ok, ConnectSocket} = ggs_network:connect(),
Pid!{self(), accept},
receive
{AcceptSocket} ->
io:format("AcceptSocket created~n"),
%eqc:quickcheck(prop_connect()),
%eqc:quickcheck(prop_receive_content(AcceptSocket, ConnectSocket))
prop_receive_content(AcceptSocket, ConnectSocket)
end.
prop_connect() ->
{Atom, _} = ggs_network:connect(),
eqc:equals(Atom, ok).
prop_receive_content(AcceptSocket, ConnectSocket) ->
%{ok, ConnectSocket} = ggs_network:connect(),
io:format("Connected~n"),
%spawn(fun() -> {ok, B} = gen_tcp:recv(ConnectSocket, 1), io:format("Data: ~s~n",[B]) end),
%timer:sleep(300),
Pid = spawn(fun() -> C = receive_content(ConnectSocket),
io:format("Content: ~s~n", [C]) end),
io:format("Before send to AcceptSocket~n"),
gen_tcp:send(AcceptSocket, "Hello\n\n"),
io:format("After send to AcceptSocket~n"),
eqc:equals(ok,ok).
receive_content(Socket) ->
receive_content_(0, "", Socket).
receive_content_(Amount, Headers, Socket) ->
{ok, Char1} = gen_tcp:recv(Socket, 1),
case Char1 of
"\n" -> case Amount of
1 -> Headers;
_ -> receive_content_(Amount + 1,
Headers ++ Char1,
Socket)
end;
_ -> receive_content_(0, Headers ++ Char1, Socket)
end.
%% Helpers
listen() ->
listen(9000).
listen(Port) ->
case gen_tcp:listen(Port, [binary, {packet, 0}, {active, false}]) of
{ok, LSock} ->
{ok, LSock};
{error, Reason} ->
io:format("Creation of listen socket failed: ~s~n", [Reason])
end.
recv(AcceptSocket) ->
gen_tcp:recv(AcceptSocket, 0).
accept(ListenSocket) ->
case gen_tcp:accept(ListenSocket) of
{ok, Socket} -> {ok, Socket};
{error, Reason} -> io:format("Error accepting listen socket~s~n", [Reason]);
_ -> io:format("Something bad happened with accept/1~n")
end.
%% new(length) == old(length) or
%% new(length) == old(length) + 1
prop_setItem() ->
ggs_db:init(),
F = (fun(T,N,K,V) -> ggs_db:setItem(T,N,K,V), ggs_db:length(T,N) end),
?FORALL({T,N,K,V},{bitstring(),bitstring(),bitstring(),bitstring()},
(ggs_db:length(T,N) + 1 == F(T,N,K,V)) or
(ggs_db:length(T,N) == F(T,N,K,V))).

View file

@ -0,0 +1,66 @@
-module(ggs_network_eqc_test).
-include_lib("../../lib/eqc/include/eqc.hrl").
-compile(export_all).
start() ->
{ok, ListenSocket} = listen(),
register(listen, ListenSocket),
Pid = spawn(fun() -> {ok, AcceptSocket} = accept(ListenSocket),
gen_tcp:send(AcceptSocket, "Hello\n\n"),
gen_tcp:close(AcceptSocket) end),
eqc:quickcheck(prop_receive_content()),
gen_tcp:close(ListenSocket).
prop_connect() ->
{Atom, _} = ggs_network:connect(),
eqc:equals(Atom, ok).
prop_receive_content() ->
{ok, ConnectSocket} = ggs_network:connect(),
Pid = spawn(fun() -> C = ggs_network:receive_content(ConnectSocket),
gen_tcp:close(ConnectSocket),
receive
{From, getcontent} -> From!{C} end end ),
Pid!{self(), getcontent},
receive
{C} -> eqc:equals(C,"Hello\n")
end.
%% Helpers
listen() ->
listen(9000).
listen(Port) ->
case gen_tcp:listen(Port, [binary, {packet, 0}, {active, false}]) of
{ok, LSock} ->
{ok, LSock};
{error, Reason} ->
io:format("Creation of listen socket failed: ~s~n", [Reason])
end.
recv(AcceptSocket) ->
gen_tcp:recv(AcceptSocket, 0).
accept(ListenSocket) ->
case gen_tcp:accept(ListenSocket) of
{ok, Socket} -> {ok, Socket};
{error, Reason} -> io:format("Error accepting listen socket~s~n", [Reason]);
_ -> io:format("Something bad happened with accept/1~n")
end.
%% new(length) == old(length) or
%% new(length) == old(length) + 1
prop_setItem() ->
ggs_db:init(),
F = (fun(T,N,K,V) -> ggs_db:setItem(T,N,K,V), ggs_db:length(T,N) end),
?FORALL({T,N,K,V},{bitstring(),bitstring(),bitstring(),bitstring()},
(ggs_db:length(T,N) + 1 == F(T,N,K,V)) or
(ggs_db:length(T,N) == F(T,N,K,V))).

View file

@ -0,0 +1,72 @@
-module(ggs_network_eqc_test).
-include_lib("../../lib/eqc/include/eqc.hrl").
-compile(export_all).
start() ->
{ok, ListenSocket} = listen(),
eqc:quickcheck(prop_connect()),
gen_tcp:close(ListenSocket),
{ok, ListenSocket2} = listen(),
eqc:quickcheck(prop_receive_content(ListenSocket2)).
prop_connect() ->
{Atom, Socket} = ggs_network:connect(),
gen_tcp:close(Socket),
eqc:equals(Atom, ok).
%% ?String++\n\n -> ok
prop_receive_content(ListenSocket) ->
F = fun(X) -> Pid2 = spawn(fun() -> {ok, AcceptSocket} = accept(ListenSocket),
gen_tcp:send(AcceptSocket, X),
gen_tcp:close(AcceptSocket) end),
{ok, ConnectSocket} = ggs_network:connect(),
Pid = spawn(fun() -> C = ggs_network:receive_content(ConnectSocket),
gen_tcp:close(ConnectSocket),
receive
{From, getcontent} -> From!{C} end end ),
Pid!{self(), getcontent},
receive
{C} -> eqc:equals(C++"\n",X) end end,
G = fun(N) -> String = integer_to_list(N) ++"\n\n",
F(String) end,
?FORALL(NaturalNumber, nat(), G(NaturalNumber)).
%% Helpers
listen() ->
listen(9000).
listen(Port) ->
case gen_tcp:listen(Port, [binary, {packet, 0}, {active, false}]) of
{ok, LSock} ->
{ok, LSock};
{error, Reason} ->
io:format("Creation of listen socket failed: ~s~n", [Reason])
end.
recv(AcceptSocket) ->
gen_tcp:recv(AcceptSocket, 0).
accept(ListenSocket) ->
case gen_tcp:accept(ListenSocket) of
{ok, Socket} -> {ok, Socket};
{error, Reason} -> io:format("Error accepting listen socket~s~n", [Reason]);
_ -> io:format("Something bad happened with accept/1~n")
end.
%% new(length) == old(length) or
%% new(length) == old(length) + 1
prop_setItem() ->
ggs_db:init(),
F = (fun(T,N,K,V) -> ggs_db:setItem(T,N,K,V), ggs_db:length(T,N) end),
?FORALL({T,N,K,V},{bitstring(),bitstring(),bitstring(),bitstring()},
(ggs_db:length(T,N) + 1 == F(T,N,K,V)) or
(ggs_db:length(T,N) == F(T,N,K,V))).

View file

@ -12,8 +12,16 @@
function init() {
GameServer.addGame(game_name, main());
GameServer.addClient(game_name, new TicTacToeClient(frames.player1.document.getElementById("p1"), GameServer));
GameServer.addClient(game_name, new TicTacToeClient(frames.player2.document.getElementById("p2"), GameServer));
GameServer.addClient(
game_name,
new TicTacToeClient(frames.player1.document.getElementById("p1"),
GameServer
));
GameServer.addClient(
game_name,
new TicTacToeClient(frames.player2.document.getElementById("p2"),
GameServer
));
}
</script>
<link rel="stylesheet" href="css/screen.css" type="text/css" media="screen">

View file

View file

@ -0,0 +1,172 @@
function playerCommand(player_id, command, args) {
var p1_id = GGS.localStorage.getItem("p1_id");
var p2_id = GGS.localStorage.getItem("p2_id");
if (command == "hi") {
hi(player_id);
} else if (command == "set" && p1_id && p2_id) {
move(player_id, args);
} else if (command == "new" && p1_id && p2_id) {
newGame();
}
}
var ROWS = 3;
function hi(player_id) {
var p1_id = GGS.localStorage.getItem("p1_id");
var p2_id = GGS.localStorage.getItem("p2_id");
if (!p1_id) {
GGS.localStorage.setItem("p1_id", player_id);
GGS.sendCommand(player_id, "welcome", "1");
} else if (!p2_id) {
GGS.localStorage.setItem("p2_id", player_id);
GGS.sendCommand(player_id, "welcome", "2");
newGame();
} else {
GGS.sendCommand(player_id, "not_welcome", "Already have 2 players on this table");
}
}
function move(player_id, args) {
var nextPlayer = GGS.localStorage.getItem("next_player");
var p1_id = GGS.localStorage.getItem("p1_id");
var p2_id = GGS.localStorage.getItem("p2_id");
var valid = false;
if(nextPlayer == 1 && player_id == p1_id) {
valid = true;
} else if (nextPlayer == 2 && player_id == p2_id) {
valid = true;
}
if (valid) {
var p = nextPlayer;
var props = JSON.parse(args);
var gameBoard = JSON.parse(GGS.localStorage.getItem("game_board"));
if (gameBoard[props.x][props.y] == 0) {
gameBoard[props.x][props.y] = p;
GGS.localStorage.setItem("game_board", JSON.stringify(gameBoard));
GGS.sendCommandToAll("game_board", boardAsString(gameBoard));
GGS.log(this.checkIfWon(gameBoard))
if (this.checkIfWon(gameBoard)) {
if (p == 1) {
GGS.sendCommand(p1_id, "winner", "You win!");
GGS.sendCommand(p2_id, "loser", "You lose!");
} else {
GGS.sendCommand(p1_id, "loser", "You lose!");
GGS.sendCommand(p2_id, "winner", "You win!");
}
} else if(allFieldsFull(gameBoard)) {
GGS.sendCommandToAll("draw", "It was a dwaw!");
}allFieldsFull(gameBoard)
if (nextPlayer == 1) {
GGS.localStorage.setItem("next_player", 2);
GGS.sendCommand(p1_id, "yourturn", "");
} else {
GGS.localStorage.setItem("next_player", 1);
GGS.sendCommand(p2_id, "yourturn", "");
}
} else {
GGS.sendCommand(player_id, "warning", "Already set, chose something else.");
}
} else {
GGS.sendCommand(player_id, "warning", "Not your turn!");
}
}
function checkIfWon(gameBoard) {
//var gameBoard = JSON.parse(GGS.localStorage.getItem("game_board"));
for (i = 0; i < ROWS; ++i) {
for (j = 0; j < ROWS; ++j) {
if (gameBoard[i][j] != 1) {
break;
}
}
if (j == ROWS) {
return true;
}
for (j = 0; j < ROWS; ++j) {
if (gameBoard[j][i] != 1) {
break;
}
}
if (j == ROWS) {
return true;
}
}
// Now check diagnols
for (i = 0; i < ROWS; ++i) {
if (gameBoard[i][i] != 1) {
break;
}
}
if (i == ROWS) {
return true;
}
for (i = 0; i < ROWS; ++i) {
if (gameBoard[i][ROWS - i - 1] != 1) {
break;
}
}
if (i == ROWS) {
return true;
}
return false;
}
function allFieldsFull(gameBoard) {
for (var i=0; i < ROWS; i++) {
for(var j=0; j < ROWS; j++) {
if (gameBoard[i][j] == 0) {
return false;
}
}
}
return true;
}
function newGame() {
// Initiate game with empty rows and columns
var gameBoard = [];
for (var i=0; i < ROWS; i++) {
gameBoard[i] = [""];
for (var j=0; j < ROWS; j++) {
gameBoard[i][j] = 0;
}
}
GGS.localStorage.setItem("game_board", JSON.stringify(gameBoard));
GGS.sendCommandToAll("new_game", "");
GGS.sendCommandToAll("game_board", boardAsString(gameBoard));
GGS.localStorage.setItem("next_player", 1);
}
function boardAsString(gameBoard) {
var out = "";
for (var i=0; i < ROWS; i++) {
for (var j=0; j < ROWS; j++) {
var p = gameBoard[i][j];
if (p == 1) {
out += "X";
} else if (p == 2) {
out +="O";
} else {
out += " ";
}
}
}
return out;
}

View file

@ -0,0 +1,249 @@
<?xml version="1.0" encoding="UTF-8"?>
<glade-interface>
<!-- interface-requires gtk+ 2.16 -->
<!-- interface-naming-policy project-wide -->
<widget class="GtkWindow" id="window1">
<property name="width_request">561</property>
<property name="height_request">521</property>
<property name="can_focus">False</property>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0.41999998688697815</property>
<property name="ypad">7</property>
<property name="label" translatable="yes">&lt;span size="x-large"&gt;GGS Tic Tac Toe&lt;/span&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="expand">True</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkTable" id="table1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="n_rows">3</property>
<property name="n_columns">3</property>
<property name="homogeneous">True</property>
<child>
<widget class="GtkButton" id="x0y0">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="on_x0y0_clicked" swapped="no"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="x1y0">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="on_x1y0_clicked" swapped="no"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="x0y1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="on_x0y1_clicked" swapped="no"/>
</widget>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="x1y1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="on_x1y1_clicked" swapped="no"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="x0y2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="on_x0y2_clicked" swapped="no"/>
</widget>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="x1y2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="on_x1y2_clicked" swapped="no"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="x2y0">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="on_x2y0_clicked" swapped="no"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="x2y1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="on_x2y1_clicked" swapped="no"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="x2y2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="on_x2y2_clicked" swapped="no"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<widget class="GtkEntry" id="adress">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
<property name="text" translatable="yes">ggs.jeena.net:9000</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</widget>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="token">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</widget>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="connectBtn">
<property name="label" translatable="yes">☎</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="on_connectBtn_clicked" swapped="no"/>
</widget>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkStatusbar" id="statusbar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">2</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -0,0 +1,166 @@
#!/usr/bin/env python
import sys, socket, thread, gobject, getpass, time, os, pango
try:
import pygtk
pygtk.require("2.16")
except:
pass
try:
import gtk
import gtk.glade
except:
sys.exit(1)
class GGSTTT:
def __init__(self):
#Set the Glade file
self.gladefile = "ttt.glade"
self.wTree = gtk.glade.XML(self.gladefile, "window1")
host = "localhost"
port = 9000
#Create our dictionay and connect it
dic = { "on_window1_destroy_event" : gtk.main_quit
,"on_x0y0_clicked" : lambda x: self.sendMove("{\"x\":0,\"y\":0}")
,"on_x0y1_clicked" : lambda x: self.sendMove("{\"x\":0,\"y\":1}")
,"on_x0y2_clicked" : lambda x: self.sendMove("{\"x\":0,\"y\":2}")
,"on_x1y0_clicked" : lambda x: self.sendMove("{\"x\":1,\"y\":0}")
,"on_x1y1_clicked" : lambda x: self.sendMove("{\"x\":1,\"y\":1}")
,"on_x1y2_clicked" : lambda x: self.sendMove("{\"x\":1,\"y\":2}")
,"on_x2y0_clicked" : lambda x: self.sendMove("{\"x\":2,\"y\":0}")
,"on_x2y1_clicked" : lambda x: self.sendMove("{\"x\":2,\"y\":1}")
,"on_x2y2_clicked" : lambda x: self.sendMove("{\"x\":2,\"y\":2}")
,"on_connectBtn_clicked" : lambda x: self.doConnect()
}
self.wTree.signal_autoconnect(dic)
self.wTree.get_widget("window1").show()
def doConnect(self):
self.setStatus("Not connected")
hostport = self.wTree.get_widget("adress").get_text()
host, port = hostport.split(":")
self.connect(host, int(port))
thread.start_new_thread(self.listen, ())
token = self.wTree.get_widget("token").get_text()
self.s.send("Server-Command: hello\n" +
"Content-Type: text\n" +
"Content-Length: %s\n" % len(token)+
"\n"+
token)
def sendMove(self, move):
print "Sending move", move
cmd = "set"
self.s.send("Game-Command: %s\n" % cmd +
"Content-Type: text\n" +
"Content-Length: %s\n" % len(move)+
"\n"+
move)
def setStatus(self, msg):
self.wTree.get_widget("statusbar").push(0, msg)
def listen(self):
msg = {}
print "listening"
fs = self.s.makefile()
while True:
line = fs.readline()
print "Received: '%s" % line.strip()
if line != "\n":
key = line.split(":")[0]
value = line.split(":")[1]
msg[key] = value.strip()
else:
msg["DATA"] = fs.read(int("%s" % msg["Content-Size"]))
print "Got data:", msg
self.protocolHandler(msg)
def protocolHandler(self, msg):
if msg["Client-Command"] == "hello":
data = msg["DATA"]
self.token, defined, table_token = data.split(",")
if defined == "false":
print "Defining game"
js = open("server.js").read()
self.wTree.get_widget("token").set_text(table_token)
self.s.send("Server-Command: define\n"+
"Content-Type: text\n" +
"Content-Length: %s\n" % str(len(js))+
"\n" +
js)
if defined == "true":
self.s.send("Game-Command: hi\n" +
"Content-Type: text\n" +
"Content-Length: 0\n"+
"\n")
elif msg["Client-Command"] == "welcome":
self.setStatus("You are player %s" % msg["DATA"])
elif msg["Client-Command"] == "warning":
self.setStatus("Warning: %s" % msg["DATA"])
elif msg["Client-Command"] == "not_welcome":
self.setStatus("You are not welcome: %s" % msg["DATA"])
elif msg["Client-Command"] == "game_board":
self.wTree.get_widget("x0y0").set_label(msg["DATA"][0])
self.wTree.get_widget("x0y1").set_label(msg["DATA"][1])
self.wTree.get_widget("x0y2").set_label(msg["DATA"][2])
self.wTree.get_widget("x1y0").set_label(msg["DATA"][3])
self.wTree.get_widget("x1y1").set_label(msg["DATA"][4])
self.wTree.get_widget("x1y2").set_label(msg["DATA"][5])
self.wTree.get_widget("x2y0").set_label(msg["DATA"][6])
self.wTree.get_widget("x2y1").set_label(msg["DATA"][7])
self.wTree.get_widget("x2y2").set_label(msg["DATA"][8])
self.wTree.get_widget("x0y0").get_child().modify_font(pango.FontDescription("sans 48"))
self.wTree.get_widget("x0y1").get_child().modify_font(pango.FontDescription("sans 48"))
self.wTree.get_widget("x0y2").get_child().modify_font(pango.FontDescription("sans 48"))
self.wTree.get_widget("x1y0").get_child().modify_font(pango.FontDescription("sans 48"))
self.wTree.get_widget("x1y1").get_child().modify_font(pango.FontDescription("sans 48"))
self.wTree.get_widget("x1y2").get_child().modify_font(pango.FontDescription("sans 48"))
self.wTree.get_widget("x2y0").get_child().modify_font(pango.FontDescription("sans 48"))
self.wTree.get_widget("x2y1").get_child().modify_font(pango.FontDescription("sans 48"))
self.wTree.get_widget("x2y2").get_child().modify_font(pango.FontDescription("sans 48"))
self.setStatus("")
elif msg["Client-Command"] == "defined":
self.s.send("Game-Command: hi\n" +
"Content-Type: text\n" +
"Content-Length: 0\n"+
"\n")
self.setStatus("")
elif msg["Client-Command"] == "lusers":
print msg
gobject.idle_add(self.updateUsers, msg["DATA"])
elif msg["Client-Command"] == "loser" or msg["Client-Command"] == "winner" or msg["Client-Command"] == "draw":
# md = gtk.MessageDialog(None,
# gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_INFO,
# gtk.BUTTONS_CLOSE, msg["DATA"])
# md.run()
# md.destroy()
self.s.send("Game-Command: new\n" +
"Content-Type: text\n" +
"Content-Length: 0\n"+
"\n")
self.setStatus(msg["DATA"])
def connect(self, host,port):
print "Connecting"
self.setStatus("Connecting")
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.connect((host, port))
self.setStatus("Connected!")
if __name__ == "__main__":
ttt = GGSTTT()
gobject.threads_init()
gtk.main()

15
games/tic-tac-toe/data.py Normal file
View file

@ -0,0 +1,15 @@
def greatest_sequence(match, pattern):
m = match
p = pattern
size = 0
max_size = 0
for p in pattern:
if m == p:
size += 1
else:
if size > max_size:
max_size = size
size = 0
return max_size

BIN
games/tic-tac-toe/data.pyc Normal file

Binary file not shown.

View file

@ -0,0 +1,15 @@
from pygame.mouse import get_pos
from point import Point
class Player(object):
def __init__(self, id, shape, board):
self.shape = shape
self.board = board
self.id = id
def turn(self):
#Ask mouse for position
board.make_turn(Point(get_pos()[0],get_pos()[1]))

View file

@ -0,0 +1,23 @@
#server.py
import json
from socket import socket, AF_INET, SOCK_STREAM
class server(object):
def __init__(self, port=None):
self.port = port
self.world = GGS.init()
self.socket = socket(AF_INET, SOCK_STREAM)
self.socket.connect(("www.???.com", 80))
def turn(self, id, index):
rows = sqrt(board.nr_of_rectangles)
x = int(index / rows)
y = int(index % rows)
json.dumps({"x": x, "y": y}
world.callCommand("tictactoe", "set", json.dumps({"x": x, "y": y}))
sent = 0
length = len(
while sent

View file

@ -0,0 +1,14 @@
import unittest
import data
class TestData(unittest.TestCase):
def setUp(self):
array = [0,1,1,1,3,4,5,2,2,3,3,3,3,3,33,4,2,2]
self.assertTrue(data.greatest_sequence(array, 3) == 5)
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,49 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<title></title>
<meta name="Author" content="Thomas Arts">
<meta name="Company" content="Quviq AB">
<meta name="Generator" content="Cocoa HTML Writer">
<meta name="CocoaVersion" content="1038.29">
<style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 14.0px; font: 17.0px 'Lucida Grande'}
p.p2 {margin: 0.0px 0.0px 0.0px 14.0px; font: 11.0px 'Lucida Grande'; min-height: 13.0px}
p.p3 {margin: 0.0px 0.0px 0.0px 14.0px; text-align: justify; font: 11.0px 'Lucida Grande'}
p.p4 {margin: 0.0px 0.0px 0.0px 28.0px; text-indent: -14.0px; font: 11.0px 'Lucida Grande'; min-height: 13.0px}
p.p5 {margin: 0.0px 0.0px 0.0px 28.0px; text-indent: -14.0px; font: 11.0px 'Lucida Grande'}
p.p6 {margin: 0.0px 0.0px 0.0px 43.0px; text-indent: -15.0px; font: 11.0px 'Lucida Grande'}
p.p7 {margin: 0.0px 0.0px 0.0px 43.0px; text-indent: -15.0px; font: 11.0px 'Lucida Grande'; min-height: 13.0px}
p.p8 {margin: 0.0px 0.0px 0.0px 28.0px; text-align: justify; text-indent: -14.0px; font: 11.0px 'Lucida Grande'}
p.p9 {margin: 0.0px 0.0px 0.0px 28.0px; text-align: justify; text-indent: -14.0px; font: 11.0px 'Lucida Grande'; min-height: 13.0px}
p.p10 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Cambria; min-height: 14.0px}
</style>
</head>
<body>
<p class="p1"><b>QuickCheck Mini</b></p>
<p class="p1"><b>End User License Agreement</b></p>
<p class="p2"><br></p>
<p class="p3"><b>This end user license agreement ("eula") is a legal agreement between you and quviq. Read it carefully before using the software. It provides a license to use the software and contains warranty information and liability disclaimers. By installing, copying, or otherwise using the software, you agree to be bound by the terms of this agreement. If you do not agree to the terms of this agreement, do not install or use the software.</b></p>
<p class="p4"><br></p>
<p class="p4"><br></p>
<p class="p5"><b>1 LICENSE GRANTS</b></p>
<p class="p4"><br></p>
<p class="p6"><b> Quviq</b> Quviq AB grants you the non-exclusive right to use <b>Quviqs</b> software program, QuickCheck Mini (the <b>"SOFTWARE"</b>).</p>
<p class="p4"><br></p>
<p class="p5"><b>2 LICENSE RESTRICTIONS</b></p>
<p class="p4"><br></p>
<p class="p6"><b> Distribution.</b> You may freely distribute copies of the <b>SOFTWARE</b> to third parties or point them to the download location at www.quviq.com.</p>
<p class="p6"><b>• Prohibition on Reverse Engineering, Decompilation, and Disassembly.</b> You may not reverse engineer, decompile, or disassemble the <b>SOFTWARE.</b></p>
<p class="p6"><b> Reservation of Rights. Quviq </b>retains all rights not expressly granted.</p>
<p class="p7"><span class="Apple-converted-space"> </span></p>
<p class="p5"><b>3 DISCLAIMER OF WARRANTY AND LIABILITY</b></p>
<p class="p4"><br></p>
<p class="p8"><b>Limited warranty.</b> The <b>SOFTWARE</b> is provided "as is", without warranty of any kind. To the maximum extent permitted by applicable law, <b>Quviq</b> hereby disclaim all warranties, either expressed or implied, including, but not limited to, the implied warranties of merchantability, fitness for a particular purpose, title, and non-infringement, with regard to the <b>SOFTWARE</b>, and the provision of or failure to provide support services. This limited warranty gives you specific legal rights. You may have others, which vary from state/jurisdiction to state/jurisdiction. </p>
<p class="p9"><br></p>
<p class="p8"><b>Limitation of liability.</b> To the maximum extent permitted by applicable law, in no event shall <b>Quviq</b> or its suppliers be liable for any special, incidental, indirect, or consequential damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or any other pecuniary loss) arising out of the use of or inability to use the <b>SOFTWARE</b> or the provision of or failure to provide support services, even if <b>quviq</b> has been advised of the possibility of such damages. In any case, <b>Quviqs</b> entire liability under any provision of this eula shall be limited to the amount actually paid by you for the <b>SOFTWARE</b>.</p>
<p class="p4"><br></p>
<p class="p10"><br></p>
</body>
</html>

File diff suppressed because one or more lines are too long

27
lib/eqc/README Normal file
View file

@ -0,0 +1,27 @@
QuickCheck Mini
This is a fully functional stripped down version of Quviq QuickCheck.
QuickCheck Mini is free of charge and the latest version can be
downloaded from Quviq's homepage. The Mini version of QuickCheck is
intended to support individuals and open source projects in obtaining
a better code quality without having to invest in the full version of
QuickCheck.
QuickCheck Mini is installed by copying the complete directory
eqc-.... into the Erlang library directory or by pointing to it by
using the code:add_patha/1 function in the Erlang distribution.
Examples are provided to illustrate how it works.
You can subscribe to quickcheck-questions@quviq.com by sending
"Subscribe" in the subject line. This is a community email list around
the use of QuickCheck.
Please note that although QuickCheck Mini does not require a licence
check with the Quviq licence server, it does check for a new version
each time it is started, and will notify you if one is available. No
other information is included in the request, and QuickCheck Mini will
start regardless of whether or not the version check succeeds, but
(because there is a short time-out) will start slightly more quickly
when it succeeds.

2
lib/eqc/doc/edoc-info Normal file
View file

@ -0,0 +1,2 @@
{packages,[]}.
{modules,[eqc,eqc_gen,eqc_symbolic]}.

385
lib/eqc/doc/eqc.html Normal file
View file

@ -0,0 +1,385 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Module eqc</title>
<link rel="stylesheet" type="text/css" href="stylesheet.css" title="EDoc">
</head>
<body bgcolor="white">
<div class="navbar"><a name="#navbar_top"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
<hr>
<h1>Module eqc</h1>
<ul class="index"><li><a href="#description">Description</a></li><li><a href="#types">Data Types</a></li><li><a href="#index">Function Index</a></li><li><a href="#functions">Function Details</a></li></ul>This module defines functions for writing and testing QuickCheck properties.
<p><b>Version:</b> 1.0.1</p>
<h2><a name="description">Description</a></h2>This module defines functions for writing and testing QuickCheck properties.
Much of the interface is provided via macros (defined in <tt>eqc.hrl</tt>).
These are documented below:
<h2><tt>?FORALL(X,Gen,Prop)</tt></h2>
Property that holds if <tt>Prop</tt> holds for all values <tt>X</tt> that
can be generated by <tt>Gen</tt>. For example,
<pre>
prop_reverse() -&gt;
?FORALL(Xs,list(int()),
lists:reverse(lists:reverse(Xs)) == Xs).
</pre>
Generators are defined using the module <a href="eqc_gen.html"><code>eqc_gen</code></a>.
<h2><tt>?IMPLIES(Pre,Prop)</tt></h2>
Property that holds if <tt>Prop</tt> holds whenever the precondition
<tt>Pre</tt> is true. The precondition must be a boolean, but <tt>Prop</tt>
can be any QuickCheck property. An implication is tested by discarding test
cases which do not satisfy the precondition. This can make testing slow,
since many more test cases may need to be generated to find 100 which
satisfy the precondition. In the worst case, QuickCheck may not be able
to find enough test cases that do satisfy the precondition, in which case
the number actually found is reported. Some preconditions may also skew
the test data badly--for example, a precondition that a list is sorted
skews the test data towards short lists, since random longer lists are
extremely unlikely to be sorted just by chance. <tt>?IMPLIES</tt> works
well for preconditions which are true with a high probability, but if the
precondition is unlikely to hold, then it is better to write a custom
generator which generates test cases where the precondition is true.
<h2><tt>?WHENFAIL(Action,Prop)</tt></h2><p>
Property that is equivalent to <tt>Prop</tt>, but performs <tt>Action</tt>
(for its side effects) when <tt>Prop</tt> fails. This can be used to
print additional information when a test case fails.</p>
<h2><a name="types">Data Types</a></h2>
<h3 class="typedecl"><a name="type-counterexample">counterexample()</a></h3>
<p><b>abstract datatype</b>: <tt>counterexample()</tt></p>
<p>A counter-example to a QuickCheck property, which can be obtained
using <a href="#counterexample-0"><code>counterexample/0</code></a> or <a href="#counterexample-1"><code>counterexample/1</code></a>, and used to repeat a test,
or test a different property in the same case. Counterexamples are represented by the values
bound by ?FORALL--for the counterexample to make sense independently, it's important that
these were generated without side-effects.</p>
<h3 class="typedecl"><a name="type-print_method">print_method()</a></h3>
<p><tt>print_method() = (list(term())) -&gt; any()</tt></p>
<p>A function for
printing statistics, which is passed a list of samples and is
expected to print statistical information about them. Print methods
are used by <a href="#collect-3"><code>collect/3</code></a> and <a href="#aggregate-3"><code>aggregate/3</code></a>.</p>
<h3 class="typedecl"><a name="type-property">property()</a></h3>
<p><b>abstract datatype</b>: <tt>property()</tt></p>
<p>QuickCheck properties, which can either be boolean
expressions, or constructed using the functions in this module.
QuickCheck properties are tested using <a href="#quickcheck-1"><code>quickcheck/1</code></a>.</p>
<h2><a name="index">Function Index</a></h2>
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#aggregate-2">aggregate/2</a></td><td>A property logically equivalent to <tt>Prop</tt>, but which collects a list of values in
each test, and displays the distribution of these values once
testing is complete.</td></tr>
<tr><td valign="top"><a href="#aggregate-3">aggregate/3</a></td><td>Like <a href="#aggregate-2"><code>aggregate/2</code></a>, but allows the user to specify how
the collected values should be printed.</td></tr>
<tr><td valign="top"><a href="#backtrace-0">backtrace/0</a></td><td>Displays a stack backtrace from the last exception QuickCheck caught.</td></tr>
<tr><td valign="top"><a href="#check-2">check/2</a></td><td>Tests the property in the case given.</td></tr>
<tr><td valign="top"><a href="#classify-3">classify/3</a></td><td>Property which is logically equivalent to <tt>Prop</tt>, but also
classifies test cases and displays the distribution of test case classes
when testing is complete.</td></tr>
<tr><td valign="top"><a href="#collect-2">collect/2</a></td><td>Equivalent to <a href="#aggregate-2"><tt>aggregate([S], Prop)</tt></a>.
</td></tr>
<tr><td valign="top"><a href="#collect-3">collect/3</a></td><td>Equivalent to <a href="#aggregate-3"><tt>aggregate(PrintMethod, [S], Prop)</tt></a>.
</td></tr>
<tr><td valign="top"><a href="#counterexample-0">counterexample/0</a></td><td>Returns the last counter-example found.</td></tr>
<tr><td valign="top"><a href="#counterexample-1">counterexample/1</a></td><td>Tests the property in the same way as <a href="#quickcheck-1"><code>quickcheck/1</code></a>, but if
a test fails, then the failing test case is returned as a counterexample.</td></tr>
<tr><td valign="top"><a href="#counterexamples-0">counterexamples/0</a></td><td>Returns a list of the counterexamples found by the last call
of <code>eqc:module</code>, paired with the name of the property that failed.</td></tr>
<tr><td valign="top"><a href="#current_counterexample-0">current_counterexample/0</a></td><td>Returns the most recent
counterexample found by QuickCheck.</td></tr>
<tr><td valign="top"><a href="#equals-2">equals/2</a></td><td>A property which holds if X and Y are equal...</td></tr>
<tr><td valign="top"><a href="#fails-1">fails/1</a></td><td>A property which succeeds when its argument fails.</td></tr>
<tr><td valign="top"><a href="#measure-3">measure/3</a></td><td>Collects the values of X while testing Prop, and if all tests
pass, displays statistics such as the minimum, average, and maximum
values, identified by the name Name.</td></tr>
<tr><td valign="top"><a href="#module-1">module/1</a></td><td>Tests all the properties exported from a module, given the module name.</td></tr>
<tr><td valign="top"><a href="#numtests-2">numtests/2</a></td><td>Property which is logically equivalent to <tt>Prop</tt>, but is
tested <tt>N</tt> times rather than 100.</td></tr>
<tr><td valign="top"><a href="#on_output-2">on_output/2</a></td><td>Supplies an output function to be used instead of io:format
when QuickCheck generates output.</td></tr>
<tr><td valign="top"><a href="#on_test-2">on_test/2</a></td><td>Attaches a function to a property which is called every time a
test passes or fails.</td></tr>
<tr><td valign="top"><a href="#quickcheck-1">quickcheck/1</a></td><td>Tests the property in 100 random cases, printing a counter-example
if one is found.</td></tr>
<tr><td valign="top"><a href="#recheck-1">recheck/1</a></td><td>Tests the property with the <i>same</i> random number seed as
the last failing call of <a href="#quickcheck-1"><code>quickcheck/1</code></a>.</td></tr>
<tr><td valign="top"><a href="#start-0">start/0</a></td><td>Equivalent to <a href="#start-1"><tt>start(true)</tt></a>.
</td></tr>
<tr><td valign="top"><a href="#start-1">start/1</a></td><td>Starts the QuickCheck server.</td></tr>
<tr><td valign="top"><a href="#stop-0">stop/0</a></td><td>Stops the QuickCheck server.</td></tr>
<tr><td valign="top"><a href="#version-0">version/0</a></td><td></td></tr>
<tr><td valign="top"><a href="#with_title-1">with_title/1</a></td><td>A printing method for collected data, which displays a title
before
the percentages of each value in the data.</td></tr>
</table>
<h2><a name="functions">Function Details</a></h2>
<h3 class="function"><a name="aggregate-2">aggregate/2</a></h3>
<div class="spec">
<p><tt>aggregate(L::list(term()), Prop::<a href="#type-property">property()</a>) -&gt; <a href="#type-property">property()</a></tt></p>
</div><p>A property logically equivalent to <tt>Prop</tt>, but which collects a list of values in
each test, and displays the distribution of these values once
testing is complete. A typical use would be to aggregate the list of command names generated
by <a href="eqc_statem.html#commands-1"><code>eqc_statem:commands/1</code></a>, in order to see how often each individual
command appeared in generated tests:
<pre>aggregate(command_names(Cmds), ...) </pre>
<p>
See also <a href="#aggregate-3"><code>aggregate/3</code></a>.
</p></p>
<h3 class="function"><a name="aggregate-3">aggregate/3</a></h3>
<div class="spec">
<p><tt>aggregate(PrintMethod::(list(term())) -&gt; any(), L::list(term()), Prop::<a href="#type-property">property()</a>) -&gt; <a href="#type-property">property()</a></tt></p>
</div><p>Like <a href="#aggregate-2"><code>aggregate/2</code></a>, but allows the user to specify how
the collected values should be printed. The <tt>PrintMethod</tt> parameter
is called with a sorted list of the collected data as an argument,
and is expected to print some statistics. A predefined printing
methods is provided to add a title to the statistics:
<pre>aggregate(with_title(T),L,Prop)</pre>. This is useful when a property contains
several calls to aggregate or collect.</p>
<h3 class="function"><a name="backtrace-0">backtrace/0</a></h3>
<div class="spec">
<p><tt>backtrace() -&gt; ok</tt></p>
</div><p>Displays a stack backtrace from the last exception QuickCheck caught. Note that
this is only possible if the exception is raised in the process in which the test
case starts. If a test case fails because of an exception in another, linked,
process, then no backtrace is available. Calls to functions in the implementation
of QuickCheck itself are not included in the backtrace.
<p>If you really need to see a backtrace from a linked process, then you can do so by
catching
the exception yourself in that process, using erlang:get_stacktrace() to obtain the
backtrace, and printing it yourself.</p></p>
<h3 class="function"><a name="check-2">check/2</a></h3>
<div class="spec">
<p><tt>check(P::<a href="#type-property">property()</a>, Values::<a href="#type-counterexample">counterexample()</a>) -&gt; bool()</tt></p>
</div><p>Tests the property in the case given. Counterexamples are generated by testing a
property using <a href="#counterexample-1"><code>counterexample/1</code></a> or <a href="#counterexample-0"><code>counterexample/0</code></a>, and contain a list
of the values bound by ?FORALL. A property tested by check should begin with the <i>same</i>
sequence of ?FORALL s as the property from which the counterexample was generated, otherwise
the results will be unpredictable. In particular, there is no check that the values
in the counterexample could actually have been generated by the ?FORALL s in the property under
test.
<p><a href="#check-2"><code>check/2</code></a> can be used without a QuickCheck licence, allowing anyone to run
tests that a licenced user has generated.</p></p>
<h3 class="function"><a name="classify-3">classify/3</a></h3>
<div class="spec">
<p><tt>classify(B::bool(), S::term(), Prop::<a href="#type-property">property()</a>) -&gt; <a href="#type-property">property()</a></tt></p>
</div><p>Property which is logically equivalent to <tt>Prop</tt>, but also
classifies test cases and displays the distribution of test case classes
when testing is complete. If the boolean is true then the current test case is
labelled with the term <tt>S</tt>,
and, after testing is complete, QuickCheck prints out the percentage of
test cases carrying each label. This can be used to check that the space
of possible test cases has been covered reasonably well. For example,
classifying test cases according to the length of a list enables one to
see whether unreasonably many lists were short. Classifying
test cases is a way to discover skewed distributions, such as can arise
from using <tt>?IMPLIES</tt>. It is good practice to check the distribution
of test data using <tt>classify</tt> or <a href="#collect-2"><code>collect/2</code></a>, at least while
properties are being developed.
<p>
Each test case can be labelled with any number of labels: QuickCheck then
displays the percentage of each label in the generated
test data.
</p>
<p>
Calls of classify or collect can be nested, in which case each call
generates its own table of distributions.
</p></p>
<h3 class="function"><a name="collect-2">collect/2</a></h3>
<div class="spec">
<p><tt>collect(S::term(), Prop::<a href="#type-property">property()</a>) -&gt; <a href="#type-property">property()</a></tt></p>
</div><p>Equivalent to <a href="#aggregate-2"><tt>aggregate([S], Prop)</tt></a>.</p>
<h3 class="function"><a name="collect-3">collect/3</a></h3>
<div class="spec">
<p><tt>collect(PrintMethod::(list(term())) -&gt; any(), S::term(), Prop::<a href="#type-property">property()</a>) -&gt; <a href="#type-property">property()</a></tt></p>
</div><p>Equivalent to <a href="#aggregate-3"><tt>aggregate(PrintMethod, [S], Prop)</tt></a>.</p>
<h3 class="function"><a name="counterexample-0">counterexample/0</a></h3>
<div class="spec">
<p><tt>counterexample() -&gt; undefined | <a href="#type-counterexample">counterexample()</a></tt></p>
</div><p>Returns the last counter-example found. See <a href="#counterexample-1"><code>counterexample/1</code></a>.</p>
<h3 class="function"><a name="counterexample-1">counterexample/1</a></h3>
<div class="spec">
<p><tt>counterexample(P::<a href="#type-property">property()</a>) -&gt; true | <a href="#type-counterexample">counterexample()</a></tt></p>
</div><p>Tests the property in the same way as <a href="#quickcheck-1"><code>quickcheck/1</code></a>, but if
a test fails, then the failing test case is returned as a counterexample.</p>
<h3 class="function"><a name="counterexamples-0">counterexamples/0</a></h3>
<div class="spec">
<p><tt>counterexamples() -&gt; list({atom(), <a href="#type-counterexample">counterexample()</a>})</tt></p>
</div><p>Returns a list of the counterexamples found by the last call
of <code>eqc:module</code>, paired with the name of the property that failed.</p>
<h3 class="function"><a name="current_counterexample-0">current_counterexample/0</a></h3>
<div class="spec">
<p><tt>current_counterexample() -&gt; <a href="#type-counterexample">counterexample()</a></tt></p>
</div><p>Returns the most recent
counterexample found by QuickCheck. This can be used while
QuickCheck is shrinking a failed test case to follow progress, or if
shrinking must be interrupted, to recover the last failed test case
that QuickCheck had found. The counterexample is fetched from a file
in the current directory.</p>
<h3 class="function"><a name="equals-2">equals/2</a></h3>
<div class="spec">
<p><tt>equals(X::any(), Y::any()) -&gt; <a href="#type-property">property()</a></tt></p>
</div><p>A property which holds if X and Y are equal... and displays
their values when a test fails.</p>
<h3 class="function"><a name="fails-1">fails/1</a></h3>
<div class="spec">
<p><tt>fails(P::<a href="#type-property">property()</a>) -&gt; <a href="#type-property">property()</a></tt></p>
</div><p>A property which succeeds when its argument fails.
Sometimes it is useful to write down properties which do <i>not</i> hold
(even though one might expect them to). This can help prevent misconceptions.
<tt>fails(P)</tt> is tested in the same way as <tt>P</tt>, but
fails only if <tt>P</tt> <i>succeeds</i> 100 times. Thus
<tt>fails(P)</tt> declares that QuickCheck should be able to find
a counter-example to property <tt>P</tt>.</p>
<h3 class="function"><a name="measure-3">measure/3</a></h3>
<div class="spec">
<p><tt>measure(Name::atom() | string(), X::number() | list(number()), Prop::<a href="#type-property">property()</a>) -&gt; <a href="#type-property">property()</a></tt></p>
</div><p>Collects the values of X while testing Prop, and if all tests
pass, displays statistics such as the minimum, average, and maximum
values, identified by the name Name. X can also be a list of values,
in which case all of them are included in the measurements.</p>
<h3 class="function"><a name="module-1">module/1</a></h3>
<div class="spec">
<p><tt>module(Mod::atom()) -&gt; list(atom())</tt></p>
</div><p>Tests all the properties exported from a module, given the module name.
Any function with arity zero whose name begins with "prop_" is treated as a
property. The result is a list of the names of the properties that
failed. See also <a href="#module-2"><code>module/2</code></a>.</p>
<h3 class="function"><a name="numtests-2">numtests/2</a></h3>
<div class="spec">
<p><tt>numtests(N::<a href="#type-int">int()</a>, Prop::<a href="#type-property">property()</a>) -&gt; <a href="#type-property">property()</a></tt></p>
</div><p>Property which is logically equivalent to <tt>Prop</tt>, but is
tested <tt>N</tt> times rather than 100. If numtests appears more than once
in a property, then the outermost use takes precedence.</p>
<h3 class="function"><a name="on_output-2">on_output/2</a></h3>
<div class="spec">
<p><tt>on_output(Fun::(string(), list(term())) -&gt; any(), Prop::<a href="#type-property">property()</a>) -&gt; <a href="#type-property">property()</a></tt></p>
</div><p>Supplies an output function to be used instead of io:format
when QuickCheck generates output. All output generated by
QuickCheck is passed to <tt>Fun</tt>, in the form of a format
string and a list of terms--the same arguments expected by
<tt>io:format</tt>. By supplying a function which does nothing,
QuickCheck can be run silently. By supplying a function which
writes to a file, all QuickCheck output can be saved.
<p>Note that output generated by user code is <i>not</i> passed to
this output function. For example, calls to io:format in the
property, or in the code under test, will generate output in the
shell as usual. This applies even to calls inside a
<tt>?WHENFAIL</tt>. If you want to redirect such output also, then
you need to modify your own code appropriately.</p>
<p>The reason that <tt>Fun</tt> is passed a format string and
arguments, rather than an already formatted string, is to make it
easier to extract information from the output without parsing
it. However, there is no guarantee that different versions of
QuickCheck will use the same format strings and term lists--you use
this information at your own risk, in other words.</p></p>
<h3 class="function"><a name="on_test-2">on_test/2</a></h3>
<div class="spec">
<p><tt>on_test(Fun::(<a href="#type-counterexample">counterexample()</a>, bool()) -&gt; any(), Prop::<a href="#type-property">property()</a>) -&gt; <a href="#type-property">property()</a></tt></p>
</div><p>Attaches a function to a property which is called every time a
test passes or fails. The arguments are the test case (a list of
values), and a boolean indicating whether or not the test
passed. Tests which are skipped (because of an
<tt>?IMPLIES(false,...)</tt>) are not included.</p>
<h3 class="function"><a name="quickcheck-1">quickcheck/1</a></h3>
<div class="spec">
<p><tt>quickcheck(P::<a href="#type-property">property()</a>) -&gt; bool()</tt></p>
</div><p>Tests the property in 100 random cases, printing a counter-example
if one is found. Initially small test cases are generated, then the
size increases as testing progresses (see <a href="eqc_gen.html"><code>eqc_gen</code></a>, <tt>?SIZED</tt>,
<a href="eqc_gen.html#resize-2"><code>eqc_gen:resize/2</code></a> for the way size affects test data generation).
The result is <tt>true</tt> if all tests succeeded (or if one failed,
and failure was expected). On success, <tt>quickcheck</tt> analyses
the distribution of test case labels. On failure, <tt>quickcheck</tt>
tries to simplify the counter-example found as far as possible (see <i>
shrinking</i>, described in <a href="eqc_gen.html"><code>eqc_gen</code></a>).</p>
<h3 class="function"><a name="recheck-1">recheck/1</a></h3>
<div class="spec">
<p><tt>recheck(Prop::<a href="#type-property">property()</a>) -&gt; bool()</tt></p>
</div><p>Tests the property with the <i>same</i> random number seed as
the last failing call of <a href="#quickcheck-1"><code>quickcheck/1</code></a>. If the property is
the same as in that last call, then the same test case will be
generated. Note that recheck repeats the test <i>and its
shrinking</i>. This can be used to adjust the shrinking strategy in
the property, then reshrink the same counterexample, perhaps to a
better result. If you just
want to repeat the <i>shrunk</i> test, then use
<pre>eqc:check(Prop,eqc:counterexample())</pre> instead.
<p><b>Note:</b> the type and behaviour of recheck changed in version 1.19.</p></p>
<h3 class="function"><a name="start-0">start/0</a></h3>
<div class="spec">
<p><tt>start() -&gt; any()</tt></p>
</div><p>Equivalent to <a href="#start-1"><tt>start(true)</tt></a>.</p>
<h3 class="function"><a name="start-1">start/1</a></h3>
<div class="spec">
<p><tt>start(Force::bool()) -&gt; pid()</tt></p>
</div><p><p>Starts the QuickCheck server. If it is already running on this
node, nothing is done.</p>
Each user can run only one instance of the QuickCheck server at a
time. If the server is already running on another Erlang node, it
will be terminated automatically if <tt>Force</tt> is
<tt>true</tt>. If another instance is running, and <tt>Force</tt> is
<tt>false</tt>, then the new instance will not start.</p>
<h3 class="function"><a name="stop-0">stop/0</a></h3>
<div class="spec">
<p><tt>stop() -&gt; any()</tt></p>
</div><p>Stops the QuickCheck server.
QuickCheck properties are tested in the QuickCheck server process, which is
spawned automatically when quickcheck is first called. Usually there is no
need to stop the QuickCheck server explicitly, but if a need does arise
then this function can be used. For example, if the shell process crashes
and is restarted, then the QuickCheck server should be stopped and restarted
too, since otherwise the server will crash when it attempts to write to the
console.</p>
<h3 class="function"><a name="version-0">version/0</a></h3>
<div class="spec">
<p><tt>version() -&gt; any()</tt></p>
</div>
<h3 class="function"><a name="with_title-1">with_title/1</a></h3>
<div class="spec">
<p><tt>with_title(Title::atom() | string()) -&gt; <a href="#type-print_method">print_method()</a></tt></p>
</div><p>A printing method for collected data, which displays a title
before
the percentages of each value in the data. It is intended to be
passed to <a href="#collect-3"><code>collect/3</code></a> or <a href="#aggregate-3"><code>aggregate/3</code></a>.</p>
<hr>
<div class="navbar"><a name="#navbar_bottom"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
<p><i>Generated by EDoc, Jun 13 2010, 13:15:30.</i></p>
</body>
</html>

342
lib/eqc/doc/eqc_gen.html Normal file
View file

@ -0,0 +1,342 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Module eqc_gen</title>
<link rel="stylesheet" type="text/css" href="stylesheet.css" title="EDoc">
</head>
<body bgcolor="white">
<div class="navbar"><a name="#navbar_top"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
<hr>
<h1>Module eqc_gen</h1>
<ul class="index"><li><a href="#description">Description</a></li><li><a href="#types">Data Types</a></li><li><a href="#index">Function Index</a></li><li><a href="#functions">Function Details</a></li></ul>
This module implements QuickCheck generators.
<p><b>Version:</b> 1.0.1</p>
<h2><a name="description">Description</a></h2>
This module implements QuickCheck generators.
QuickCheck generators are used to generate random test data for
QuickCheck properties. A generator specifies three things at the same
time:
<ul> <li> A <i>set</i> of values that can be generated,</li>
<li> A <i>probability distribution</i> on that set,</li>
<li> A way of <i>shrinking</i> generated values to similar,
smaller values---used after a test fails, to enable
QuickCheck to search for a similar, but simpler failing case.</li>
</ul>
QuickCheck permits constants to be used as generators for their own value,
and also permits tuples, records, and lists containing generators to be
used as generators for values of the same form. For example,
<pre> {int(),bool()} </pre>
is a generator that generates random pairs of integers and booleans.
<p>
Many of the functions in this module are usually used via macros, defined
in <tt>eqc.hrl</tt>. These macros are listed here.
<h2><tt>?LET(Pat,G1,G2)</tt></h2>
Generates a value from <tt>G1</tt>,
binds it to <tt>Pat</tt>, then generates a value from <tt>G2</tt>
(which may refer to the variables bound in <tt>Pat</tt>).
<p>The
result is shrunk by <i>first</i> shrinking the value generated by
<tt>G1</tt> while the test still fails, then shrinking the value
generated by <tt>G2</tt>. It is thus better to write
<tt>?LET({X,Y},{G1,G2},G3)</tt> than
<tt>?LET(X,G1,?LET(Y,G2,G3))</tt> (provided <tt>G2</tt> does
not depend on <tt>X</tt>), since in the first case shrinking can
shrink <tt>G1</tt> a bit, shrink <tt>G2</tt>, then shrink
<tt>G1</tt> some more, while in the second case <tt>G1</tt>
cannot be shrunk further once shrinking <tt>G2</tt> has begun.</p>
<h2><tt>?SIZED(Size,G)</tt></h2>
Binds the variable <tt>Size</tt> to the current size parameter for
generation. <tt>G</tt> may use <tt>Size</tt> in any way to control the
size of generated data. However, as <tt>Size</tt> increases,
the set of possible values that <tt>G</tt> can generate should also
increase. <tt>Size</tt> is always a natural number, and increases during
QuickCheck testing from a small value up to about 40. See also
<a href="#resize-2"><code>resize/2</code></a> and <a href="#pick-2"><code>pick/2</code></a>.
<h2><tt>?SUCHTHAT(X,G,P)</tt></h2>
Generates values <tt>X</tt> from <tt>G</tt> such that the condition <tt>P</tt> is true.
Should only be used if the probability that <tt>P</tt> holds is reasonably high for values
generated by <tt>G</tt>--otherwise generation may be slow, and the
distribution of generated values may be skewed. For example,
<pre>?SUCHTHAT(Xs,list(int()),lists:sort(Xs)==Xs)</pre>
generates predominantly very short lists, since the probability that a random longer list
will just happen to be sorted is very low. If no value is found within 100 attempts,
then ?SUCHTHAT exits.
<h2><tt>?LETSHRINK(Pat,G1,G2)</tt></h2>
This behaves in the same way as <tt>?LET(Pat,G1,G2)</tt>, <i>except</i>
that <tt>G1</tt> must generate a <i>list</i> of values, and each one of these
values is added as a possible shrinking of the result. This is intended for
use in generating tree-like structures. For example,
<pre>
?LETSHRINK([L,R],[tree(),tree()],{branch,L,R})</pre>
generates a tree node <tt>{branch,L,R}</tt>, which can shrink to either
<tt>L</tt> or <tt>R</tt>.
<h2><tt>?LAZY(G)</tt></h2>
A generator equivalent to its argument, but which is always cheap to construct. To be used,
for example, in recursive generators to avoid building a huge generator, only a small part
of which will be used.
</p>
<h2><a name="types">Data Types</a></h2>
<h3 class="typedecl"><a name="type-box">box()</a></h3>
<p><b>abstract datatype</b>: <tt>box(A)</tt></p>
<p>Boxes are not supported in this version of QuickCheck.</p>
<h3 class="typedecl"><a name="type-gen">gen()</a></h3>
<p><b>abstract datatype</b>: <tt>gen(A)</tt></p>
<p>A QuickCheck generator for values of type A.
QuickCheck generators are first-class
values, and can be used repeatedly to generate many different values.</p>
<h3 class="typedecl"><a name="type-proplist">proplist()</a></h3>
<p><tt>proplist() = list({atom(), term()})</tt></p>
<p>A property list associating values with names.
See the standard module <tt>proplists</tt>.</p>
<h2><a name="index">Function Index</a></h2>
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#binary-0">binary/0</a></td><td>Generates a binary of random size.</td></tr>
<tr><td valign="top"><a href="#binary-1">binary/1</a></td><td>Generates a binary of a given size in bytes.</td></tr>
<tr><td valign="top"><a href="#bitstring-0">bitstring/0</a></td><td>Generates a list of bits in a bitstring.</td></tr>
<tr><td valign="top"><a href="#bitstring-1">bitstring/1</a></td><td>Generates a bitstring of a given size in bits.</td></tr>
<tr><td valign="top"><a href="#bool-0">bool/0</a></td><td>Generates a random boolean.</td></tr>
<tr><td valign="top"><a href="#char-0">char/0</a></td><td>Generates a random character.</td></tr>
<tr><td valign="top"><a href="#choose-2">choose/2</a></td><td>Generates a number in the range M to N.</td></tr>
<tr><td valign="top"><a href="#default-2">default/2</a></td><td>Adds a default value to a generator, to be chosen half the time.</td></tr>
<tr><td valign="top"><a href="#elements-1">elements/1</a></td><td>Generates an element of the list argument.</td></tr>
<tr><td valign="top"><a href="#eval-1">eval/1</a></td><td>Evaluates terms of the form <tt>{call,Module,Function,Args}</tt> anywhere in its
argument, replacing them by the result of the corresponding function call.</td></tr>
<tr><td valign="top"><a href="#eval-2">eval/2</a></td><td>Like <a href="#eval-1"><code>eval/1</code></a>, but also replaces symbolic variables, that is,
terms of the form <tt>{var,V}</tt>, by their corresponding values in the
property list.</td></tr>
<tr><td valign="top"><a href="#frequency-1">frequency/1</a></td><td>Makes a weighted choice between the generators in its argument, such that the
probability of choosing each generator is proportional to the weight paired with it.</td></tr>
<tr><td valign="top"><a href="#function0-1">function0/1</a></td><td>Generates a function of no arguments with result generated by <tt>G</tt>.</td></tr>
<tr><td valign="top"><a href="#function1-1">function1/1</a></td><td>Generates a function of one argument with result generated by <tt>G</tt>.</td></tr>
<tr><td valign="top"><a href="#int-0">int/0</a></td><td>Generates a small integer (with absolute value bounded by the generation size).</td></tr>
<tr><td valign="top"><a href="#is_generator-1">is_generator/1</a></td><td>Returns true if the argument is a QuickCheck generator.</td></tr>
<tr><td valign="top"><a href="#largeint-0">largeint/0</a></td><td>Generates an integer from a large range.</td></tr>
<tr><td valign="top"><a href="#list-1">list/1</a></td><td>Generates a list of elements generated by its argument.</td></tr>
<tr><td valign="top"><a href="#nat-0">nat/0</a></td><td>Generates a small natural number (bounded by the generation size).</td></tr>
<tr><td valign="top"><a href="#non_empty-1">non_empty/1</a></td><td>Make sure that the generated value is not empty.</td></tr>
<tr><td valign="top"><a href="#noshrink-1">noshrink/1</a></td><td>Generates the same values as <tt>G</tt>, but these values are never
shrunk.</td></tr>
<tr><td valign="top"><a href="#oneof-1">oneof/1</a></td><td>Generates a value using a randomly chosen element of the list of generators.</td></tr>
<tr><td valign="top"><a href="#orderedlist-1">orderedlist/1</a></td><td>Generates an ordered list of elements generated by <tt>G</tt>.</td></tr>
<tr><td valign="top"><a href="#real-0">real/0</a></td><td>Generates a real number.</td></tr>
<tr><td valign="top"><a href="#resize-2">resize/2</a></td><td>Binds the generation size parameter to <tt>Size</tt> within <tt>G</tt>.</td></tr>
<tr><td valign="top"><a href="#return-1">return/1</a></td><td>Constructs a generator that always generates the value
<tt>X</tt>.</td></tr>
<tr><td valign="top"><a href="#sample-1">sample/1</a></td><td>Prints 11 values randomly generated by <tt>G</tt>, for sizes ranging
from 10 to 20.</td></tr>
<tr><td valign="top"><a href="#sampleshrink-1">sampleshrink/1</a></td><td>Prints a value generated by <tt>G</tt>, followed by one way of shrinking it.</td></tr>
<tr><td valign="top"><a href="#shuffle-1">shuffle/1</a></td><td>Shuffles a list and shrinks to the unshuffled list.</td></tr>
<tr><td valign="top"><a href="#vector-2">vector/2</a></td><td>Generates a list of the given length, with elements generated by <tt>G</tt>.</td></tr>
</table>
<h2><a name="functions">Function Details</a></h2>
<h3 class="function"><a name="binary-0">binary/0</a></h3>
<div class="spec">
<p><tt>binary() -&gt; <a href="#type-gen">gen(binary())</a></tt></p>
</div><p>Generates a binary of random size. The binary shrinks both in
size as well as in content. If you consider the
binary as a representation of a number, then each shrinking step
will result in a smaller-or-equal number.</p>
<h3 class="function"><a name="binary-1">binary/1</a></h3>
<div class="spec">
<p><tt>binary(NrBytes::<a href="#type-int">int()</a>) -&gt; <a href="#type-gen">gen(binary())</a></tt></p>
</div><p>Generates a binary of a given size in bytes. When shrinking,
the size is unchanged, but content shrinks like <a href="#binary-0"><code>binary/0</code></a>.</p>
<h3 class="function"><a name="bitstring-0">bitstring/0</a></h3>
<div class="spec">
<p><tt>bitstring() -&gt; <a href="#type-gen">gen(<a href="#type-bitstring">bitstring()</a>)</a></tt></p>
</div><p>Generates a list of bits in a bitstring. For Erlang release R12B and
later.
The bitstring shrinks both in
size as well as in content. If you consider the
bitstring as a representation of a number, then each shrinking step
will result in a smaller-or-equal number.</p>
<h3 class="function"><a name="bitstring-1">bitstring/1</a></h3>
<div class="spec">
<p><tt>bitstring(NrBits::<a href="#type-int">int()</a>) -&gt; <a href="#type-gen">gen(<a href="#type-bitstring">bitstring()</a>)</a></tt></p>
</div><p>Generates a bitstring of a given size in bits. For Erlang
release R12B and later. When shrinking,
the size is unchanged, but content shrinks like <a href="#bitstring-0"><code>bitstring/0</code></a>.</p>
<h3 class="function"><a name="bool-0">bool/0</a></h3>
<div class="spec">
<p><tt>bool() -&gt; <a href="#type-gen">gen(bool())</a></tt></p>
</div><p>Generates a random boolean. Shrinks to false.</p>
<h3 class="function"><a name="char-0">char/0</a></h3>
<div class="spec">
<p><tt>char() -&gt; <a href="#type-gen">gen(char())</a></tt></p>
</div><p>Generates a random character. Shrinks to a, b or c.</p>
<h3 class="function"><a name="choose-2">choose/2</a></h3>
<div class="spec">
<p><tt>choose(M, N::integer()) -&gt; <a href="#type-gen">gen(integer())</a></tt></p>
</div><p>Generates a number in the range M to N.
The result shrinks towards smaller absolute values.</p>
<h3 class="function"><a name="default-2">default/2</a></h3>
<div class="spec">
<p><tt>default(Default::A, G::<a href="#type-gen">gen(A)</a>) -&gt; <a href="#type-gen">gen(A)</a></tt></p>
</div><p>Adds a default value to a generator, to be chosen half the time. Any
other value shrinks to the default.</p>
<h3 class="function"><a name="elements-1">elements/1</a></h3>
<div class="spec">
<p><tt>elements(Xs::list(A)) -&gt; <a href="#type-gen">gen(A)</a></tt></p>
</div><p>Generates an element of the list argument. Shrinking chooses an earlier element.</p>
<h3 class="function"><a name="eval-1">eval/1</a></h3>
<div class="spec">
<p><tt>eval(Term::term()) -&gt; term()</tt></p>
</div><p>Evaluates terms of the form <tt>{call,Module,Function,Args}</tt> anywhere in its
argument, replacing them by the result of the corresponding function call.
This is useful when, for example, test data is of an abstract datatype, and
we want to know how it was generated, rather than its representation--it is
much clearer to see that a test failed for <tt>sets:new()</tt> (that is
<tt>{call,sets,new,[]}</tt>), for example,
than for its representation.
We write <tt>?FORALL(X,TGen,...eval(X)...)</tt>, where <tt>TGen</tt>
generates terms containing calls, so that test cases are displayed in this
form, but the actual test data is the result of evaluating the calls.</p>
<h3 class="function"><a name="eval-2">eval/2</a></h3>
<div class="spec">
<p><tt>eval(Env::<a href="#type-proplist">proplist()</a>, T::term()) -&gt; term()</tt></p>
</div><p>Like <a href="#eval-1"><code>eval/1</code></a>, but also replaces symbolic variables, that is,
terms of the form <tt>{var,V}</tt>, by their corresponding values in the
property list. This should be a list of pairs of atoms and values. For example,
<tt>eval([{x,3}],{var,x})</tt> evaluates to 3.</p>
<h3 class="function"><a name="frequency-1">frequency/1</a></h3>
<div class="spec">
<p><tt>frequency(FGs::list({integer(), <a href="#type-gen">gen(A)</a>})) -&gt; <a href="#type-gen">gen(A)</a></tt></p>
</div><p>Makes a weighted choice between the generators in its argument, such that the
probability of choosing each generator is proportional to the weight paired with it.
The
weights should be non-negative integers and sum to a positive value. A generator
with a weight of zero will not be chosen.</p>
<h3 class="function"><a name="function0-1">function0/1</a></h3>
<div class="spec">
<p><tt>function0(G::<a href="#type-gen">gen(A)</a>) -&gt; <a href="#type-gen">gen(() -&gt; A)</a></tt></p>
</div><p>Generates a function of no arguments with result generated by <tt>G</tt>.</p>
<h3 class="function"><a name="function1-1">function1/1</a></h3>
<div class="spec">
<p><tt>function1(G::<a href="#type-gen">gen(A)</a>) -&gt; <a href="#type-gen">gen((term()) -&gt; A)</a></tt></p>
</div><p>Generates a function of one argument with result generated by <tt>G</tt>.
The generated function is pure--will always return the same result for the same argument--
and the result depends randomly on the argument.</p>
<h3 class="function"><a name="int-0">int/0</a></h3>
<div class="spec">
<p><tt>int() -&gt; <a href="#type-gen">gen(integer())</a></tt></p>
</div><p>Generates a small integer (with absolute value bounded by the generation size).</p>
<h3 class="function"><a name="is_generator-1">is_generator/1</a></h3>
<div class="spec">
<p><tt>is_generator(X::any()) -&gt; bool()</tt></p>
</div><p>Returns true if the argument is a QuickCheck generator.</p>
<h3 class="function"><a name="largeint-0">largeint/0</a></h3>
<div class="spec">
<p><tt>largeint() -&gt; any()</tt></p>
</div><p>Generates an integer from a large range.</p>
<h3 class="function"><a name="list-1">list/1</a></h3>
<div class="spec">
<p><tt>list(G::<a href="#type-gen">gen(A)</a>) -&gt; <a href="#type-gen">gen(list(A))</a></tt></p>
</div><p>Generates a list of elements generated by its argument. Shrinking drops elements
from the list. The length of the list varies up to one third of the generation size parameter.</p>
<h3 class="function"><a name="nat-0">nat/0</a></h3>
<div class="spec">
<p><tt>nat() -&gt; <a href="#type-gen">gen(integer())</a></tt></p>
</div><p>Generates a small natural number (bounded by the generation size).</p>
<h3 class="function"><a name="non_empty-1">non_empty/1</a></h3>
<div class="spec">
<p><tt>non_empty(G::<a href="#type-gen">gen(A)</a>) -&gt; <a href="#type-gen">gen(A)</a></tt></p>
</div><p>Make sure that the generated value is not empty.
For example when creating a list of integers, but the list should always
contain at least one element <tt>non_empty(list(int()))</tt>.</p>
<h3 class="function"><a name="noshrink-1">noshrink/1</a></h3>
<div class="spec">
<p><tt>noshrink(G::<a href="#type-gen">gen(A)</a>) -&gt; <a href="#type-gen">gen(A)</a></tt></p>
</div><p>Generates the same values as <tt>G</tt>, but these values are never
shrunk.</p>
<h3 class="function"><a name="oneof-1">oneof/1</a></h3>
<div class="spec">
<p><tt>oneof(Gs::list(<a href="#type-gen">gen(A)</a>)) -&gt; <a href="#type-gen">gen(A)</a></tt></p>
</div><p>Generates a value using a randomly chosen element of the list of generators.</p>
<h3 class="function"><a name="orderedlist-1">orderedlist/1</a></h3>
<div class="spec">
<p><tt>orderedlist(G::<a href="#type-gen">gen(A)</a>) -&gt; <a href="#type-gen">gen(list(A))</a></tt></p>
</div><p>Generates an ordered list of elements generated by <tt>G</tt>.</p>
<h3 class="function"><a name="real-0">real/0</a></h3>
<div class="spec">
<p><tt>real() -&gt; <a href="#type-gen">gen(float())</a></tt></p>
</div><p>Generates a real number.</p>
<h3 class="function"><a name="resize-2">resize/2</a></h3>
<div class="spec">
<p><tt>resize(Size::integer(), G::<a href="#type-gen">gen(A)</a>) -&gt; <a href="#type-gen">gen(A)</a></tt></p>
</div><p>Binds the generation size parameter to <tt>Size</tt> within <tt>G</tt>.
Size should never be negative.</p>
<h3 class="function"><a name="return-1">return/1</a></h3>
<div class="spec">
<p><tt>return(X::A) -&gt; <a href="#type-gen">gen(A)</a></tt></p>
</div><p>Constructs a generator that always generates the value
<tt>X</tt>. Most values can also be used as generators for
themselves, making <tt>return</tt> unnecessary, but
<tt>return(X)</tt> may be more efficient than using <tt>X</tt> as a
generator, since when <tt>return(X)</tt> is used then QuickCheck
does not traverse <tt>X</tt> searching for values to be intepreted
specially.</p>
<h3 class="function"><a name="sample-1">sample/1</a></h3>
<div class="spec">
<p><tt>sample(G::<a href="#type-gen">gen(A)</a>) -&gt; ok</tt></p>
</div><p>Prints 11 values randomly generated by <tt>G</tt>, for sizes ranging
from 10 to 20.</p>
<h3 class="function"><a name="sampleshrink-1">sampleshrink/1</a></h3>
<div class="spec">
<p><tt>sampleshrink(G::<a href="#type-gen">gen(A)</a>) -&gt; ok</tt></p>
</div><p>Prints a value generated by <tt>G</tt>, followed by one way of shrinking it.
Each following line displays a list of values that the <i>first</i> value on the
previous line can be shrunk to in one step. Thus the output traces the leftmost path
through the shrinking tree.</p>
<h3 class="function"><a name="shuffle-1">shuffle/1</a></h3>
<div class="spec">
<p><tt>shuffle(List::list(A)) -&gt; <a href="#type-gen">gen(list(A))</a></tt></p>
</div><p>Shuffles a list and shrinks to the unshuffled list.</p>
<h3 class="function"><a name="vector-2">vector/2</a></h3>
<div class="spec">
<p><tt>vector(K::integer(), G::<a href="#type-gen">gen(A)</a>) -&gt; <a href="#type-gen">gen(list(A))</a></tt></p>
</div><p>Generates a list of the given length, with elements generated by <tt>G</tt>.</p>
<hr>
<div class="navbar"><a name="#navbar_bottom"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
<p><i>Generated by EDoc, Jun 13 2010, 13:15:30.</i></p>
</body>
</html>

View file

@ -0,0 +1,156 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Module eqc_symbolic</title>
<link rel="stylesheet" type="text/css" href="stylesheet.css" title="EDoc">
</head>
<body bgcolor="white">
<div class="navbar"><a name="#navbar_top"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
<hr>
<h1>Module eqc_symbolic</h1>
<ul class="index"><li><a href="#description">Description</a></li><li><a href="#index">Function Index</a></li><li><a href="#functions">Function Details</a></li></ul>
This module implements QuickCheck generators and utility functions for
symbolic calls.
<p><b>Version:</b> 1.0.1</p>
<h2><a name="description">Description</a></h2><p>
This module implements QuickCheck generators and utility functions for
symbolic calls.</p>
<p>In test case generation it is often an advantage to postpone calling
functions in the subject under test. In a test one is interested in
the actual function that is called as well as its evaluated result.
If one would evaluate the result already at generation time, then the actual
call is not visible in the QuickCheck counter example shown in a
failing test.</p>
<p>For example, when testing a data structure like the OTP library sets.erl,
one may need more information than just the value to detect what goes
wrong with the following property:</p>
<pre>
prop_sets() -&gt;
?FORALL({S1,S2},{set(),set()},
begin
L1 = sets:to_list(S1),
L2 = sets:to_list(S2),
sets:intersection(S1,S2) ==
sets:from_list(L1--(L1--L2))
end).
</pre>
which will fail with for example the following counter example:
<pre>
Failed! After 132 tests.
Shrinking.......(7 times)
{{set,2,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
{{[],[],[],[],[],[],[],[],[],[],[],[-15,33],[],[],[],[]}}},
{set,3,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
{{[0],[],[],[],[],[],[],[],[],[],[],[33,-15],[],[],[],[]}}}}
false
</pre>
We would really need to understand the internal representation of sets in order to understand which
sets we have generated and even if we know that, we have no clue which operations were used to
create those sets.
This is were symbolic representations help a lot. We would create a recursive generator that
creates symbolic sets and use the following property instead:
<pre>
prop_sets() -&gt;
?FORALL({SymbS1,SymbS2},{set(),set()},
begin
S1 = eval(SymbS1),
S2 = eval(SymbS2),
L1 = sets:to_list(S1),
L2 = sets:to_list(S2),
sets:intersection(S1,S2) ==
sets:from_list(L1--(L1--L2))
end).
This would then result in a more readable error message:
Shrinking..........(10 times)
{{call,sets,from_list,[[6,-10]]},{call,sets,from_list,[[0,-10,6]]}}
false
</pre>
Symbolic representation of function calls provides us with
<ul>
<li>Better readable counter examples</li>
<li>No need to break abstraction by using clean interface to code
under test</li>
<li>Better possibilities for shrinking when defining generators</li>
</ul>
<h2><a name="index">Function Index</a></h2>
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#defined-1">defined/1</a></td><td>Checks whether a term can be evaluated without raising an exception.</td></tr>
<tr><td valign="top"><a href="#eval-1">eval/1</a></td><td>Evaluates terms of the form <tt>{call,Module,Function,Args}</tt> anywhere in its
argument, replacing them by the result of the corresponding function call.</td></tr>
<tr><td valign="top"><a href="#eval-2">eval/2</a></td><td>Like <a href="#eval-1"><code>eval/1</code></a>, but also replaces symbolic variables, that is,
terms of the form <tt>{var,V}</tt>, by their corresponding values in the
property list.</td></tr>
<tr><td valign="top"><a href="#pretty_print-1">pretty_print/1</a></td><td>Pretty printing of symbolic terms.</td></tr>
<tr><td valign="top"><a href="#pretty_print-2">pretty_print/2</a></td><td>Pretty printing of symbolic terms within given environment.</td></tr>
<tr><td valign="top"><a href="#well_defined-1">well_defined/1</a></td><td>Generates a well defined symbolic value.</td></tr>
</table>
<h2><a name="functions">Function Details</a></h2>
<h3 class="function"><a name="defined-1">defined/1</a></h3>
<div class="spec">
<p><tt>defined(E::term()) -&gt; bool()</tt></p>
</div><p>Checks whether a term can be evaluated without raising an exception.
Some symbolic terms may raise an exception when evaluating, e.g., division by zero would
raise an exception, thus <tt>eval({call,erlang,'div',[1,0]})</tt> raises an exception as well.</p>
<h3 class="function"><a name="eval-1">eval/1</a></h3>
<div class="spec">
<p><tt>eval(Term::term()) -&gt; term()</tt></p>
</div><p>Evaluates terms of the form <tt>{call,Module,Function,Args}</tt> anywhere in its
argument, replacing them by the result of the corresponding function call.
This is useful when, for example, test data is of an abstract datatype, and
we want to know how it was generated, rather than its representation--it is
much clearer to see that a test failed for <tt>sets:new()</tt> (that is
<tt>{call,sets,new,[]}</tt>), for example,
than for its representation.
We write <tt>?FORALL(X,TGen,...eval(X)...)</tt>, where <tt>TGen</tt>
generates terms containing calls, so that test cases are displayed in this
form, but the actual test data is the result of evaluating the calls.</p>
<h3 class="function"><a name="eval-2">eval/2</a></h3>
<div class="spec">
<p><tt>eval(Env::<a href="#type-proplist">proplist()</a>, T::term()) -&gt; term()</tt></p>
</div><p>Like <a href="#eval-1"><code>eval/1</code></a>, but also replaces symbolic variables, that is,
terms of the form <tt>{var,V}</tt>, by their corresponding values in the
property list. This should be a list of pairs of atoms and values. For example,
<tt>eval([{x,3}],{var,x})</tt> evaluates to 3.</p>
<h3 class="function"><a name="pretty_print-1">pretty_print/1</a></h3>
<div class="spec">
<p><tt>pretty_print(Symb::term()) -&gt; string()</tt></p>
</div><p>Pretty printing of symbolic terms.
A symbolic value like <tt>{call,sets,union,[{call,sets,new,[]},{call,sets,from_list,[[1,2]]}]}</tt>
is transformed to the string \"sets:union(sets:new(),from_list([1,2]))\".</p>
<h3 class="function"><a name="pretty_print-2">pretty_print/2</a></h3>
<div class="spec">
<p><tt>pretty_print(Env::<a href="#type-proplist">proplist()</a>, Symb::term()) -&gt; string()</tt></p>
</div><p>Pretty printing of symbolic terms within given environment.
Like <a href="#pretty_print-1"><code>pretty_print/1</code></a>, but also replaces symbolic variables, that is,
terms of the form <tt>{var,V}</tt>, by their corresponding values in the
property list. This should be a list of pairs of atoms and values. For example,
<tt>eval([{x,3}],{var,x})</tt> is pretty printed to \"3\".</p>
<h3 class="function"><a name="well_defined-1">well_defined/1</a></h3>
<div class="spec">
<p><tt>well_defined(G::<a href="#type-gen">gen(A)</a>) -&gt; A</tt></p>
</div><p>Generates a well defined symbolic value.
A value is well defined if evaluating it does not raise an exception.</p>
<hr>
<div class="navbar"><a name="#navbar_bottom"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
<p><i>Generated by EDoc, Jun 13 2010, 13:15:30.</i></p>
</body>
</html>

BIN
lib/eqc/doc/erlang.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

17
lib/eqc/doc/index.html Normal file
View file

@ -0,0 +1,17 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Overview</title>
</head>
<frameset cols="20%,80%">
<frame src="modules-frame.html" name="modulesFrame" title="">
<frame src="overview-summary.html" name="overviewFrame" title="">
<noframes>
<h2>This page uses frames</h2>
<p>Your browser does not accept frames.
<br>You should go to the <a href="overview-summary.html">non-frame version</a> instead.
</p>
</noframes>
</frameset>
</html>

View file

@ -0,0 +1,14 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Overview</title>
<link rel="stylesheet" type="text/css" href="stylesheet.css" title="EDoc">
</head>
<body bgcolor="white">
<h2 class="indextitle">Modules</h2>
<table width="100%" border="0" summary="list of modules">
<tr><td><a href="eqc.html" target="overviewFrame" class="module">eqc</a></td></tr>
<tr><td><a href="eqc_gen.html" target="overviewFrame" class="module">eqc_gen</a></td></tr>
<tr><td><a href="eqc_symbolic.html" target="overviewFrame" class="module">eqc_symbolic</a></td></tr></table>
</body>
</html>

View file

@ -0,0 +1,53 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>QuickCheck Mini </title>
<link rel="stylesheet" type="text/css" href="stylesheet.css" title="EDoc">
</head>
<body bgcolor="white">
<div class="navbar"><a name="#navbar_top"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
<h1>QuickCheck Mini </h1>
<p>Copyright © Quviq AB, 2006-2010. </p>
<p><b>Version:</b> 1.0.1 </p>
<p>
QuickCheck is a specification-based testing tool for Erlang. QuickCheck Mini is
a powerful, but restricted version of QuickCheck. It is released free of charge for
anyone interested in learning more about QuickCheck as well as for open source
developers that want to ship their code with simple QuickCheck properties.</p>
<p>In order to learn more about QuickCheck, please visit http://www.quviq.com/.
There is also a mailing list discussing QuickCheck issues, which you can subscribe to by sending an email to quickcheck-questions@quviq.com with subject "Subscribe".</p>
<h2>QuickCheck</h2>
Programs
are tested by writing <i>properties</i> in the source code, such as
<pre>
prop_reverse() -&gt;
?FORALL(Xs,list(int()),
lists:reverse(lists:reverse(Xs)) == Xs).
</pre>
Properties are tested by calling
<pre>
eqc:quickcheck(prop_reverse())
</pre><p>
which generates 100 random test cases and checks that the property
returns <tt>true</tt> in every case.</p>
<p>
Functions for writing properties are found in module <a href="eqc.html"><code>eqc</code></a>,
while functions for writing test data generators (such as
<tt>list(int())</tt> above) are found in module <a href="eqc_gen.html"><code>eqc_gen</code></a>. Parts
of the interface are provided via macros, which are defined in
<tt>eqc.hrl</tt>--this file should be included in every module which
uses QuickCheck. This header file also imports much of the QuickCheck
API, so it can be used without explicit module names.
</p>
<hr>
<div class="navbar"><a name="#navbar_bottom"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
<p><i>Generated by EDoc, Jun 13 2010, 13:15:30.</i></p>
</body>
</html>

39
lib/eqc/doc/overview.edoc Normal file
View file

@ -0,0 +1,39 @@
@title QuickCheck Mini
@version {@version}
@copyright Quviq AB, 2006-2010.
@doc
QuickCheck is a specification-based testing tool for Erlang. QuickCheck Mini is
a powerful, but restricted version of QuickCheck. It is released free of charge for
anyone interested in learning more about QuickCheck as well as for open source
developers that want to ship their code with simple QuickCheck properties.
In order to learn more about QuickCheck, please visit http://www.quviq.com/.
There is also a mailing list discussing QuickCheck issues, which you can subscribe to by sending an email to quickcheck-questions@quviq.com with subject "Subscribe".
<h2>QuickCheck</h2>
Programs
are tested by writing <i>properties</i> in the source code, such as
<pre>
prop_reverse() ->
?FORALL(Xs,list(int()),
lists:reverse(lists:reverse(Xs)) == Xs).
</pre>
Properties are tested by calling
<pre>
eqc:quickcheck(prop_reverse())
</pre>
which generates 100 random test cases and checks that the property
returns <tt>true</tt> in every case.
<p>
Functions for writing properties are found in module {@link eqc},
while functions for writing test data generators (such as
<tt>list(int())</tt> above) are found in module {@link eqc_gen}. Parts
of the interface are provided via macros, which are defined in
<tt>eqc.hrl</tt>--this file should be included in every module which
uses QuickCheck. This header file also imports much of the QuickCheck
API, so it can be used without explicit module names.
</p>

View file

@ -0,0 +1,11 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Overview</title>
<link rel="stylesheet" type="text/css" href="stylesheet.css" title="EDoc">
</head>
<body bgcolor="white">
<h2 class="indextitle">Packages</h2>
<table width="100%" border="0" summary="list of packages"></table>
</body>
</html>

View file

@ -0,0 +1,55 @@
/* standard EDoc style sheet */
body {
font-family: Verdana, Arial, Helvetica, sans-serif;
margin-left: .25in;
margin-right: .2in;
margin-top: 0.2in;
margin-bottom: 0.2in;
color: #000000;
background-color: #ffffff;
}
h1,h2 {
margin-left: -0.2in;
}
div.navbar {
background-color: #add8e6;
padding: 0.2em;
}
h2.indextitle {
padding: 0.4em;
background-color: #add8e6;
}
h3.function,h3.typedecl {
background-color: #add8e6;
padding-left: 1em;
}
div.spec {
margin-left: 2em;
background-color: #eeeeee;
}
a.module,a.package {
text-decoration:none
}
a.module:hover,a.package:hover {
background-color: #eeeeee;
}
ul.definitions {
list-style-type: none;
}
ul.index {
list-style-type: none;
background-color: #eeeeee;
}
/*
* Minor style tweaks
*/
ul {
list-style-type: square;
}
table {
border-collapse: collapse;
}
td {
padding: 3
}

7
lib/eqc/ebin/eqc.app Normal file
View file

@ -0,0 +1,7 @@
{application,eqc,
[{mod,{eqc,[]}},
{description,"Quviq QuickCheck Mini"},
{vsn,"1.0.1"},
{modules,[eqc,eqc_gen,eqc_symbolic,eqc_warn]},
{applications,[kernel,stdlib,inets]}]}.

View file

@ -0,0 +1,227 @@
%% This file tests that generators produce the right kind of data. It
%% also illustrates how to write a QuickSpec specification of a
%% datatype (such as the generator datatype).
-module(generators_eqc).
-include_lib("eqc/include/eqc.hrl").
-compile(export_all).
%% Generate most combinations of generator functions, as a symbolic
%% generator--for example:
%% {call,oneof,[{{call,bitstring,14},{call,elements,[15]}}]}
%% We control the size of the symbolic generator, by limiting the
%% nesting depth.
generator() ->
?SIZED(N,generator(N)).
generator(0) ->
% base case: no nested generators.
oneof([constant(),
binary,{call,binary,nat()},
bitstring,{call,bitstring,nat()},
bool,char,
?SUCHTHAT({call,choose,Lo,Hi},
{call,choose,int(),int()},
Lo=<Hi),
{call,elements,non_empty_list(constant())},
int,largeint,nat,real]);
generator(N) ->
% recursive case: reduce the size of nested compound generators.
N1 = N div 4,
?LAZY(oneof([generator(0),compound_generator(N1)])).
constant() ->
oneof([int(),atom()]).
atom() ->
elements([a,b,c,d]).
term() ->
?LET(G,generator(),generated_by(G)).
compound_generator(N) ->
Smaller = generator(N),
oneof([?LETSHRINK([Sm],[Smaller],?LET(Def,term(),{call,default,Sm,Def})),
?LETSHRINK(Gs,non_empty_list(Smaller),
{call,frequency,[{positive(),G} || G <- Gs]}),
?LETSHRINK([Sm],[Smaller],{call,list,Sm}),
?LETSHRINK([Sm],[Smaller],{call,orderedlist,Sm}),
?LETSHRINK([Sm],[?SUCHTHAT(Sm,Smaller,not often_empty(Sm))],
{call,non_empty,Sm}),
?LETSHRINK([Sm],[Smaller],{call,noshrink,Sm}),
?LETSHRINK(Gs,non_empty_list(Smaller),
{call,oneof,Gs}),
?LETSHRINK([Sm],[Smaller],{call,resize,choose(0,N),Sm}),
?LETSHRINK(L,list(constant()),{call,shuffle,L}),
?LETSHRINK(Gs,short_list(Smaller),
list_to_tuple(Gs)),
?LETSHRINK([Sm],[Smaller],{call,vector,choose(0,4),Sm})]).
%% To keep the size of generators under control, it is not enough to
%% restrict nesting depth. We also want lists of arguments to oneof,
%% frequency etc to be reasonably short.
short_list(G) ->
?SIZED(Size,
resize(Size div 3 + 1,
list(resize(Size,G)))).
positive() ->
?LET(N,nat(),N+1).
non_empty_list(G) ->
non_empty(short_list(G)).
%% When we generate {call,non_empty,G}, we need to know that G is
%% reasonably likely to produce a non-empty value... otherwise we may
%% loop when we try to use this generator!
often_empty([]) ->
true;
often_empty(<<>>) ->
true;
often_empty({call,vector,N,G}) ->
N==0;
often_empty({call,binary,N}) ->
N==0;
often_empty({call,bitstring,N}) ->
N==0;
often_empty({call,noshrink,G}) ->
often_empty(G);
often_empty({call,oneof,Gs}) ->
% conservative
lists:any(fun often_empty/1,Gs);
often_empty({call,frequency,WGs}) ->
% conservative
lists:any(fun often_empty/1, [G || {_,G} <- WGs]);
often_empty({call,default,G,V}) ->
often_empty(G) andalso often_empty(V);
often_empty({call,resize,N,G}) ->
N<4 orelse often_empty(G);
often_empty({call,shuffle,L}) ->
L==[];
often_empty(_) ->
false.
%% The values generated by a symbolic generator.
generated_by(A) when is_atom(A) ->
case erlang:function_exported(eqc_gen,A,0) of
true ->
eqc_gen:A();
false ->
A
end;
generated_by(T) when is_tuple(T), size(T)>0 ->
case tuple_to_list(T) of
[call,F|Args] ->
erlang:apply(eqc_gen,F,[generated_by(G) || G <- Args]);
Gs ->
list_to_tuple([generated_by(G) || G <- Gs])
end;
generated_by([H|T]) ->
[generated_by(H)|generated_by(T)];
generated_by(X) ->
X.
%% Check that a generated value corresponds to its generator.
is(binary,B) ->
is_binary(B);
is({call,binary,N},B) ->
is_binary(B) andalso size(B)==N;
is(bitstring,B) ->
is_bitstring(B);
is({call,bitstring,N},B) ->
is_bitstring(B) andalso size(B) == N div 8;
is(bool,B) ->
B==true orelse B==false;
is(char,C) ->
is_integer(C) andalso 0=<C andalso C=<255;
is({call,choose,Lo,Hi},N) ->
is_integer(N) andalso Lo =< N andalso N =< Hi;
is({call,default,G,D},X) ->
is(G,X) orelse X==D;
is({call,elements,L},X) ->
lists:member(X,L);
is({call,frequency,WGs},X) ->
lists:any(fun({_,G})->is(G,X) end,WGs);
is(int,N) ->
is_integer(N) andalso abs(N) =< 100;
is(largeint,N) ->
is_integer(N);
is(nat,N) ->
is_integer(N) andalso N>=0;
is(real,N) ->
is_float(N);
is({call,list,G},L) ->
is_list(L) andalso
lists:all(fun(X)->is(G,X) end,L);
is({call,orderedlist,G},L) ->
is_list(L) andalso
lists:all(fun(X)->is(G,X) end,L) andalso
lists:sort(L) == L;
is({call,non_empty,G},X) ->
is(G,X) andalso X/=[] andalso X/=<<>>;
is({call,noshrink,G},X) ->
is(G,X);
is({call,oneof,Gs},X) ->
lists:any(fun(G) -> is(G,X) end,Gs);
is({call,resize,_,G},X) ->
is(G,X);
is({call,shuffle,L},X) ->
is_list(X) andalso lists:sort(X) == lists:sort(L);
is({call,vector,N,G},V) ->
is_list(V) andalso length(V)==N andalso
lists:all(fun(X)->is(G,X) end,V);
is(GT,T) when is_tuple(GT) ->
is_tuple(T) andalso size(T)==size(GT) andalso
lists:all(fun({G,X})->is(G,X) end,
lists:zip(tuple_to_list(GT),
tuple_to_list(T)));
is(Const,X) when is_atom(Const); is_integer(Const) ->
Const == X.
%% The properties.
%% Generate symbolic generators, and report on the distribution of
%% generator functions used.
prop_generator() ->
?FORALL(G,generator(),
aggregate(generator_types(G),true)).
generator_types(G) when is_tuple(G) ->
case tuple_to_list(G) of
[call,frequency,WArgs] ->
[T || {_,G1} <- WArgs, T <- generator_types(G1)];
[call,F|Args] ->
[F|[T || A <- Args,
T <- generator_types(A)]];
L ->
[T || X <- L,
T <- generator_types(X)]
end;
generator_types(N) when is_integer(N) ->
[integer_constant];
generator_types(X) when is_float(X) ->
[float_constant];
generator_types(B) when is_binary(B) ->
[binary];
generator_types(B) when is_bitstring(B) ->
[bitstring];
generator_types(L) when is_list(L) ->
[list|[T || X <- L, T <- generator_types(X)]];
generator_types(X) ->
[X].
%% For each kind of generator, use it to generate a value, and check
%% that the value matches the generator. This tests the generators
%% (and our generator-generators!) pretty thoroughly.
prop_correct_types() ->
?FORALL(G,generator(),
?FORALL(X,generated_by(G),is(G,X))).

View file

@ -0,0 +1,40 @@
%%% File : ip_checksum.erl
%%% Author : Ulf Norell <ulf.norell@quviq.com>,
%%% Thomas Arts <thomas.arts@quviq.com>
%%% Description : Implementation of IP checksums.
%%% Created : 7 Jun 2010 by Ulf Norell
-module(ip_checksum).
-export([checksum/1, checksum/2, sum/2, pad/2, add/2, negate/1]).
checksum(Bin) ->
checksum(Bin, 16).
checksum(Bin, N) ->
negate(sum(pad(Bin, N), N)).
% Sum a binary of N bit words in ones complement representation.
sum(Bin, N) ->
lists:foldl(fun(A, B) -> add(A, B) end, <<0:N>>,
[ <<A:N>> || <<A:N>> <= Bin ]).
% Add two numbers in ones complement representation.
add(A, B) ->
N = bit_size(A),
<<X:N>> = A,
<<Y:N>> = B,
Carry = (X + Y) div (1 bsl N),
<<(X + Y + Carry):N>>.
%% invert all bits... as simple as that.
negate(BitString) ->
<< <<(1-Bit):1>> || <<Bit:1>> <= BitString >>.
pad(Binary, Bits) ->
PaddingLength =
case bit_size(Binary) rem Bits of
0 -> 0;
N -> Bits - N
end,
<<Binary/bits, 0:PaddingLength>>.

View file

@ -0,0 +1,218 @@
%%% File : ip_checksum_eqc.erl
%%% Author : Ulf Norell <ulf.norell@quviq.com>,
%%% Thomas Arts <thomas.arts@quviq.com>
%%% Description : QuickCheck properties for ip_checksum.erl
%%% Created : 7 Jun 2010 by Ulf Norell
-module(ip_checksum_eqc).
-include_lib("eqc/include/eqc.hrl").
-compile(export_all).
% == Testing IP checksum implementations ==
% In RFC 1071 efficient algorithms are discussed for computing the internet
% checksum, also known as IP checksum. Whenever you implement efficient
% algorithms, an error may sneak through.
% This article is meant to be used as test driven development specification for
% anyone that wants to implement one of the algorithms of RFC 1071 or even a new
% one to compute the IP checksum. The article can also be read as an example of
% specifying something without revealing its implementation details; a good
% example of using QuickCheck specifications.
% Whether you write your code in Erlang, C or Java, we assume that you can build
% an interface to a module called ip_checksum.erl in which Erlang functions
% either are the functions under test or call the functions under test.
% === IP Checksum ===
% The IP checksum is the 16 bit one's complement of the one's complement sum of
% all 16 bit words in the header.
% Ones complement is a way of representing negative numbers (see
% [http://en.wikipedia.org/wiki/Signed_number_representations#Ones.27_complement
% WikiPedia] for more details).
% The IP checksum uses 16 bit words. In 16 bits you can represent the numbers 0
% to 65535. The idea with ones complement is to use half the numbers in this
% interval for representing negative numbers. Thus, 0 up to 32767 are the
% positive numbers and 65535 is -0, or an alternative representation of zero.
% The number 65534 is -1 etc. Until 32768 which is -32767. Hence the interval
% -32767 up to 32767 can be represented.
% In the remainder of this article we will present properties for functions that
% you probably would like to test. The properties are always parametrized by the
% word size.
% === Utility functions ===
% First we define some functions that will come in handy.
% The maximum number that can be represented in ''N'' bits. In the ones
% complement interpretation this will be the negative zero.
max_int(N) ->
(1 bsl N) - 1.
negative_zero(N) ->
max_int(N).
% === Ones complement ===
% The first function we might want to check is the ones' complement of a word,
% which in ones' complement representation corresponds to the negation. We
% assume we have a function '''ip_checksum:negate/1''' implemented that takes a
% bit string as input and computes its ones' complement.
% Looking at the specification of ones' complement representation above we can
% see that adding the ones' complement representation of a number and the
% representation of its negation results in the representation of -0. For
% instance, the representation of -2 is 65533 and the representation of 2 is 2.
% Adding these we get 65535 which is the representation of -0. We use this
% property to test the implementation of the '''negate/2''' function.
prop_negate(N) ->
?FORALL(I, choose(0, max_int(N)),
begin
<<CI:N>> = ip_checksum:negate(<<I:N>>),
equals(negative_zero(N), I + CI)
end).
% The property above is parameterized by the word size N. We'll want to test
% our properties for a range of different word sizes, so we define a general
% function to transform a parameterized property to a property choosing random
% word sizes.
random_word_size(Prop) ->
?FORALL(N, choose(1, 64), Prop(N)).
prop_negate() ->
random_word_size(fun prop_negate/1).
% === Padding ===
% It is not clear from the specification presented above, but if you need to
% compute the checksum of a list of bytes in base 16, then there should be an
% even number of bytes. Likewise, if we would like to do ones complement in 32
% bits base, we would need to extend a sequence of bytes such that it is
% divisible by 4.
% Extending a bit string such that it is divisible by the base is called padding.
% We assume that you implemented a padding function that added the necessary
% bits, given a bit string. We assume this function to be implemented as
% '''ip_checksum:pad/1''' taking a bit string as argument and returning a new
% bit string which is an extended version with as many zero bits as needed.
prop_padding() ->
random_word_size(fun prop_padding/1).
prop_padding(N) ->
?FORALL(BitString, bitstring(),
begin
Bits = bit_size(BitString),
<<B:Bits/bits, Padded/bits>> = ip_checksum:pad(BitString,N),
Zeros = bit_size(Padded),
% If this property fails we need to know what the pad function actually
% returned in order to understand what went wrong. This is what the
% ?WHENFAIL macro is for.
?WHENFAIL(io:format("B = ~w\nPadded = ~w\nZeros = ~w\n",
[B, Padded, Zeros]),
((Bits + Zeros) rem N) == 0 andalso % the new length is divisible by N
B == BitString andalso % we don't change the bit string
<<0:Zeros>> == Padded andalso % we pad with zeros
Zeros < N % we don't pad more than we have to
)
end).
% Confident that the padding function works we can write a generator for
% correctly padded bit strings.
padded_bitstring(N) ->
?LET(Bits, bitstring(), ip_checksum:pad(Bits, N)).
% An alternative definition of this generator would not use the padding
% function, but rather first generate the length of the bit string and then
% pass that to the bit string generator (see below). The advantage of the former
% definition is that it behaves better when shrinking. The version below will
% regenerate the bit string everytime the length is getting shorter.
padded_bitstring_2(N) ->
?LET(Len, nat(), bitstring(N * Len)).
% === Ones complement sum ===
% The ones complement sum is computed by adding a number of words in ones
% complement representation. We assume this function to be implemented as
% '''ip_checksum:sum/2''' which takes a bit string as first argument and a
% word size as second argument. We assume that padding is done outside the
% sum function and only test that the function works for bit strings of
% which the length is divisible by the given word size.
% Because of our test driven development method, we have already tested the
% '''negate/1''' function and therefore trust this function in our property.
% Remember that adding the representations of a number and its negation yields
% -0. This is in fact also true if we use one's complement addition rather than
% simply adding the representations (which is not the same thing). So if we
% concatenate a bit string with the negation of its sum, the sum of the
% resulting bit string should be -0 if our implementation of '''sum/2''' is
% correct. By testing the sum function in this way we don't have to worry about
% specifying the intricacies of ones' complement arithmetic (except for the
% fact that X + (-X) = -0).
prop_sum() ->
random_word_size(fun prop_sum/1).
prop_sum(N) ->
?FORALL(Bin, padded_bitstring(N),
begin
Sum = ip_checksum:sum(Bin, N),
CSum = ip_checksum:negate(Sum),
equals(ip_checksum:sum(<<CSum/bits, Bin/bits>>, N),
<<(negative_zero(N)):N>>)
end).
% === Checksum ===
% After computing ones' complement sum, one has to take the ones' complement of
% the result to compute the checksum. Of course, we have all ingredients in
% house to do so, but in case you implement both functions as one you would
% like to test the final result '''ip_checksum:checksum/2''' with a bit string
% and word size as arguments.
% We first test that the '''checksum/2''' function takes care of padding, by
% checking that padding the bitstring before passing it to '''checksum/2'''
% doesn't change the result.
prop_checksum_pad() ->
random_word_size(fun prop_checksum_pad/1).
prop_checksum_pad(N) ->
?FORALL(Bits, bitstring(),
equals(ip_checksum:checksum(Bits, N),
ip_checksum:checksum(ip_checksum:pad(Bits, N), N))).
% We can test the '''checksum/2''' function in the same way as we tested
% '''sum/2''' above. Taking a bit string and prepending its checksum should
% result in a bit string whose checksum is zero. Here's why:
%
% % definition of checksum
% checksum(Bits) == -sum(Bits)
%
% checksum(checksum(Bits) ++ Bits) == {def. of checksum}
% -sum(-sum(Bits) ++ Bits) == {sum(Xs ++ Ys) == sum(Xs) + sum(Ys)}
% -(-sum(Bits) + sum(Bits)) == {adding -X and X}
% -(-0) ==
% 0
%
% Note that due to padding, the property doesn't hold if we append the checksum
% rather than prepending it.
prop_checksum() ->
random_word_size(fun prop_checksum/1).
prop_checksum(N) ->
?FORALL(Bin, bitstring(),
begin
Sum = ip_checksum:checksum(Bin, N),
equals(ip_checksum:checksum(<<Sum/bits, Bin/bits>>, N),
<<0:N>>)
end).

View file

@ -0,0 +1,223 @@
%%% File : lists_eqc.erl
%%% Author : Thomas Arts <thomas.arts@quviq.com>
%%% Ulf Norell <ulf.norell@quviq.com>
%%% Description : QuickCheck tests for some functions from the lists library.
%%% Created : 23 Mar 2010 by Thomas Arts <thomas.arts@quviq.com>
-module(lists_eqc).
-include_lib("eqc/include/eqc.hrl").
-compile(export_all).
% === Testing lists:delete/2 ===
% The lists:delete/2 function removes an element from a list. Here's a property
% we might want for this function: after you've removed an element from a list
% it's not there anymore. The corresponding QuickCheck property is:
prop_delete_0() ->
?FORALL({X, Xs}, {int(), list(int())},
not lists:member(X, lists:delete(X, Xs))).
% Checking this property for 100 random values and lists it might actually
% pass.
test_delete_0() ->
quickcheck(prop_delete_0()).
% However, rerunning the property a few more times will reveal a problem:
test_delete_0_more() ->
quickcheck(numtests(2000,prop_delete_0())).
% We get output looking like this:
% 74> lists_eqc:test_delete_0_more().
% ............................................................................
% ............................................................................
% ............................................................................
% ............................................................................
% ....................................................Failed! After 377 tests.
% {7,[-6,1,23,24,7,7]}
% Shrinking..(2 times)
% {7,[7,7]}
% false
% There is a problem with our specification. lists:delete/2 only removes the
% first occurrence of the element, something our specification fails to take
% into account.
% Before fixing the problem in the specification, it's worth thinking about why
% we needed so many tests to find the bug. In order to find the bug we need to
% generate a value and a list such that the value appears twice in the list.
% What's the probability of that? To answer that question we can write a new
% property:
prop_member_probability() ->
?FORALL({X, Xs}, {int(), list(int())},
collect(lists:member(X, Xs), true)).
% This property always succeeds, but for every test case it records whether the
% generated value appears (even once) in the list. Running the property a large
% number of times reveals that the probability that a random value appears in a
% random list is around 8%. No wonder it's hard to find a test case where it
% appears at least twice!
% To make it easier to find a failing case, we can change our property to only
% look at cases where the value appears at least once in the list. To do this
% we use the ?IMPLIES macro.
prop_delete_1() ->
?FORALL({X, Xs}, {int(), list(int())},
?IMPLIES(lists:member(X, Xs),
not lists:member(X, lists:delete(X, Xs)))).
% Now the output will look something like this:
% 102> eqc:quickcheck(lists_eqc:prop_delete_1()).
% xxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx.
% .xxxxxxxxxxxxxx.xx.xxxxxxx.xxxxxxxxxxx.xxxxxxxxxxxxxxxxxxx.xxx.xxxxxxxxxxxxx
% xxxxxxxxx.xxxxxxxxxxx.xxxxx.xxxxxxxxxxxxxxxxxxxxxx.xx.xxxxxxxxxxxx.xxxxxxxxx
% xxxxxxxxxxxxxxxxxxxxxxxxx..xxxxx.xxxx.xxxxxxxxx.xxxxxFailed! After 22 tests.
% {1,[31,35,35,34,-21,-13,7,1,2,1,-35,2]}
% Shrinking.....(5 times)
% {1,[1,1]}
% false
% The 'x's represent test cases that were discarded because they didn't satisfy
% the condition of the ?IMPLIES.
% Now that we have a property that fails reliably we can use it to document our
% misconception about the behaviour of lists:delete/2. The fails/1 function
% takes a property that is expected to fail and fails it doesn't.
prop_delete_2() ->
fails(
?FORALL({X, Xs}, {int(), list(int())},
?IMPLIES(lists:member(X, Xs),
not lists:member(X, lists:delete(X, Xs))))).
% Let's fix the specification. One possibility would be to count the number of
% occurrences of the value we're removing before and after calling
% lists:delete/2, but we can write a more precise specification quite elegantly:
prop_delete_3() ->
?FORALL({Xs, X, Ys}, {list(int()), int(), list(int())},
?IMPLIES(not lists:member(X, Xs),
equals(lists:delete(X, Xs ++ [X] ++ Ys),
Xs ++ Ys))).
% The equals function compares its arguments for equality and, if they're not
% equal, prints the arguments. This lets us see what lists:delete actually
% returned in the case we get a failing test case. Try removing the ?IMPLIES
% and run the property to see what it looks like.
% === Testing lists:seq/3 ===
% The lists:seq/2 function has recently changed in Erlang. The new
% specification allows lists:seq(1,0) and returns [] in that case. Copying the
% specification from the manual still reveals an error
% eqc:quickcheck(lists_eqc:prop_seq0()) due to the fact that a special case is
% overlooked. The fix is in prop_seq().
% This is the property according to the documentation. This property fails with
% a badarith exception on the test case {0, 0, 0}. The problem is that the
% specification of the length is not correct for increment 0.
prop_seq0() ->
?FORALL({From,To,Incr},{int(),int(),int()},
case catch lists:seq(From,To,Incr) of
{'EXIT',_} ->
(To < From-Incr andalso Incr > 0) orelse
(To > From-Incr andalso Incr < 0) orelse
(Incr==0 andalso From /= To);
List ->
is_list(List) andalso
length(List) == (To-From+Incr) div Incr
end).
% This is the property as it holds.
prop_seq() ->
?FORALL({From,To,Incr},{int(),int(),int()},
case catch lists:seq(From,To,Incr) of
{'EXIT',_} ->
(To < From-Incr andalso Incr > 0) orelse
(To > From-Incr andalso Incr < 0) orelse
(Incr==0 andalso From /= To);
List when Incr /= 0 ->
is_list(List) andalso
length(List) == (To-From+Incr) div Incr;
List when Incr == 0 ->
length(List) == 1
end).
% This is probably how one would like seq to behave.
prop_seq_wish(Seq) ->
?FORALL({From,To,Incr},{int(),int(),int()},
case catch Seq(From,To,Incr) of
[] -> Incr > 0 andalso From > To orelse
Incr < 0 andalso From < To;
[_] when Incr == 0 -> From == To;
List when Incr /= 0 andalso is_list(List) ->
length(List) == (To-From+Incr) div Incr;
{'EXIT', _} ->
Incr == 0 andalso From /= To;
_ ->
false
end).
prop_seq_wish() ->
prop_seq_wish(fun lists:seq/3).
% Here is a reference implementation satisfying prop_seq_wish.
seq(From, To, 0) when From /= To ->
exit("seq: increment 0");
seq(From, From, _Incr) ->
[From];
seq(From, To, Incr) when From > To andalso Incr > 0 ->
[];
seq(From, To, Incr) when From < To andalso Incr < 0 ->
[];
seq(From, To, Incr) ->
[From | seq(From + Incr, To, Incr)].
prop_seq_wish_granted() ->
prop_seq_wish(fun seq/3).
% The previous properties only specifies the length of the result of
% lists:seq/3. We also want to make sure that it contains the right elements.
% In particular, if lists:seq(From, To, Incr) returns a non-empty list, the
% first element of the list should be From, and the difference between adjacent
% elements should be Incr. We've already tested that the list has the right
% number of elements so we don't have to worry about when the list ends.
% First some helper functions:
% We're only interested in non-empty results of seq/3.
is_cons([_|_]) -> true;
is_cons(_) -> false.
% We want to look at the difference between adjacent elements.
diff_adjacent([X,Y|Xs]) ->
[Y - X|diff_adjacent([Y|Xs])];
diff_adjacent(_) ->
[].
% We use ?IMPLIES to ignore the cases when lists:seq does not return a
% non-empty list. To make sure we still get interesting test cases we collect
% the lengths of the results and the increments we've chosen.
prop_seq_elements() ->
?FORALL({From, To, Incr}, {int(), int(), int()},
begin
Seq = (catch lists:seq(From, To, Incr)),
?IMPLIES(is_cons(Seq),
begin
Adj = diff_adjacent(Seq),
?WHENFAIL(io:format("Seq = ~w\nAdj = ~w", [Seq, Adj]),
% When you have several collects in the same property you can give them
% names using collect/3 and with_title/1 to distinguish them.
% We divide the actual numbers by 5 to reduce the number of different
% values collected.
collect(with_title(lengths), length(Seq) div 5,
collect(with_title(incr), Incr div 5,
hd(Seq) == From andalso
lists:all(fun(D) -> D == Incr end, Adj)
))
)
end)
end).

View file

@ -0,0 +1,146 @@
%%% File : sets_eqc.erl
%%% Author : Thomas Arts <thomas.arts@quviq.com>
%%% Description : QuickCheck properties for sets.erl
%%% Based on "Testing Data Structures with QuickCheck"
%%% Created : 24 Mar 2010 by Thomas Arts
-module(sets_eqc).
-include_lib("eqc/include/eqc.hrl").
-compile(export_all).
%% Create a generator for the opaque type "set". The generator will generate
%% symbolic calls which when evaluated computes a set. Each symbolic call has
%% the form {call, Module, Function, Arguments} and are evaluated using the
%% function eval/1.
%% To avoid generating infinite symbolic representations we pass the size
%% parameter to the generator and use it to make sure we stop eventually.
set(G) ->
?SIZED(Size,well_defined(set(Size,G))).
set(0,G) ->
oneof([{call,sets,new,[]},
{call,sets,from_list,[list(G)]}]);
set(N,G) ->
frequency(
[{5,set(0,G)},
{3, ?LAZY(?LETSHRINK([Set],[set(N-1,G)],
{call,sets,add_element,[G, Set]}))},
{1, ?LAZY(?LETSHRINK([Set],[set(N-1,G)],
{call,sets,del_element,[G, Set]}))},
{1, ?LAZY(?LETSHRINK([Set1,Set2],[set(N div 2,G),set(N div 2,G)],
{call,sets,union,[Set1, Set2]}))},
{1, ?LAZY(?LETSHRINK(Sets,list(set(N div 3,G)),
{call,sets,union,[Sets]}))},
{1, ?LAZY(?LETSHRINK([Set1,Set2],[set(N div 2,G),set(N div 2,G)],
{call,sets,intersection,[Set1, Set2]}))},
{1, ?LAZY(?LETSHRINK(Sets,?LET(L,nat(),vector(L+1,set(N div (L+1),G))),
{call,sets,intersection,[Sets]}))},
{1, ?LAZY(?LETSHRINK([Set1,Set2],[set(N div 2,G),set(N div 2,G)],
{call,sets,subtract,[Set1, Set2]}))},
{1, ?LAZY(?LETSHRINK([Set],[set(N div 2,G)],
{call,sets,filter,[function1(bool()), Set]}))}]).
%% The next step is to define a model interpretation, i.e. a simplified,
%% obviously correct implementation of the data type. In this case we use
%% usorted lists.
model(S) ->
lists:sort(sets:to_list(S)).
%% Define the set operations on the model.
madd_element(E,S) ->
lists:usort([E|S]).
mdel_element(E,S) ->
S -- [E].
msize(S) ->
length(S).
mis_element(E,S) ->
lists:member(E,S).
munion(Ss) ->
lists:usort(lists:append(Ss)).
mintersection(Sets) ->
[ E || E <- lists:usort(lists:append(Sets)),
lists:all(fun(Set) -> lists:member(E,Set) end, Sets)].
mis_disjoint(S1,S2) ->
mintersection([S1,S2]) == [].
mfilter(Pred,S) ->
[ E || E <- S, Pred(E)].
%% Define one property for each operation. We parameterize the properties on
%% the generator for the elements. To make it easy to run the properties we
%% also define special versions that use integers.
%% Each property have the same basic form: we check that a given operation
%% on sets has the same behaviour as the corresponding model operation.
prop_create() -> prop_create(int()).
prop_create(G) ->
?FORALL(Set,set(G),
sets:is_set(eval(Set))).
prop_add_element() -> prop_add_element(int()).
prop_add_element(G) ->
?FORALL({E,Set},{G,set(G)},
model(sets:add_element(E,eval(Set))) == madd_element(E,model(eval(Set)))).
prop_del_element() -> prop_del_element(int()).
prop_del_element(G) ->
?FORALL({E,Set},{G,set(G)},
model(sets:del_element(E,eval(Set))) == mdel_element(E,model(eval(Set)))).
prop_union2() -> prop_union2(int()).
prop_union2(G) ->
?FORALL({Set1,Set2},{set(G),set(G)},
model(sets:union(eval(Set1),eval(Set2))) == munion([model(eval(Set1)),model(eval(Set2))])).
prop_union() -> prop_union(int()).
prop_union(G) ->
?FORALL(Sets,list(set(G)),
model(sets:union(eval(Sets))) == munion([model(Set) || Set<-eval(Sets)])).
prop_intersection2() -> prop_intersection2(int()).
prop_intersection2(G) ->
?FORALL({Set1,Set2},{set(G),set(G)},
model(sets:intersection(eval(Set1),eval(Set2))) ==
mintersection([model(eval(Set1)),model(eval(Set2))])).
prop_intersection() -> prop_intersection(int()).
prop_intersection(G) ->
?FORALL(Sets,eqc_gen:non_empty(list(set(G))),
model(sets:intersection(eval(Sets))) == mintersection([model(Set) || Set<-eval(Sets)])).
prop_size() -> prop_size(int()).
prop_size(G) ->
?FORALL(Set,set(G),
sets:size(eval(Set)) == msize(model(eval(Set)))).
prop_is_element() -> prop_is_element(int()).
prop_is_element(G) ->
?FORALL({E,Set},{G,set(G)},
sets:is_element(E,eval(Set)) == mis_element(E,model(eval(Set)))).
prop_is_disjoint() -> prop_is_disjoint(int()).
prop_is_disjoint(G) ->
?FORALL({Set1,Set2},{set(G),set(G)},
sets:is_disjoint(eval(Set1),eval(Set2)) == mis_disjoint(model(eval(Set1)),model(eval(Set2)))).
prop_filter() -> prop_filter(int()).
prop_filter(G) ->
?FORALL({Pred,Set},{function1(bool()),set(G)},
model(sets:filter(Pred,eval(Set))) == mfilter(Pred,model(eval(Set)))).

63
lib/eqc/include/eqc.hrl Normal file
View file

@ -0,0 +1,63 @@
% Generated file--see release:make()
% eqc_macros.hrl
-define(DELAY(X),fun()->X end).
-define(FORCE(X),(X)()).
-define(LET(X,E,E2),eqc_gen:bind(E,fun(X)->E2 end)).
-define(SIZED(S,G),eqc_gen:sized(fun(S)->G end)).
-define(SUCHTHAT(X,G,P),eqc_gen:suchthat(G,fun(X)->P end)).
-define(SUCHTHATMAYBE(X,G,P),eqc_gen:suchthatmaybe(G,fun(X)->P end)).
-define(SHRINK(G,Gs),eqc_gen:shrinkwith(G,?DELAY(Gs))).
-define(LETSHRINK(Es,Gs,E), eqc_gen:letshrink(Gs,fun(Es) -> E end)).
-define(LAZY(G),eqc_gen:lazy(?DELAY(G))).
-define(IMPLIES(Pre,Prop),eqc:implies(Pre,??Pre,?DELAY(Prop))).
-define(FORALL(X,Gen,Prop),eqc:forall(Gen,fun(X)->Prop end)).
-define(WHENFAIL(Action,Prop),eqc:whenfail(fun(_) -> Action end,?LAZY(Prop))).
-define(TRAPEXIT(E),eqc:trapexit(?DELAY(E))).
-define(TIMEOUT(Limit,Prop),eqc:timeout(Limit,?LAZY(Prop))).
-define(ALWAYS(N,P),eqc:always(N,?DELAY(P))).
-define(SOMETIMES(N,P),eqc:sometimes(N,?DELAY(P))).
% eqc_imports.hrl
-import(eqc_gen,
[pick/1,pick/2,
includeif/2,return/1,applygen/2,
noshrink/1,shrinkings/1,shrinking_path/2,
timeout/2,
resize/2,
parameter/1, parameter/2, with_parameter/3, with_parameters/2,
choose/2,
shuffle/1,
sample/1, sampleshrink/1,
oneof/1, frequency/1,
non_empty/1,
elements/1, growingelements/1, list/1, shrink_list/1, vector/2,
function0/1, function1/1, function2/1, function3/1, function4/1,
bool/0, maybe/1, char/0, int/0, shrink_int/3, nat/0, largeint/0,
real/0, orderedlist/1,
binary/0, binary/1, bitstring/0, bitstring/1,
default/2, weighted_default/2,
seal/1,open/1,peek/1,
fault/2, fault_rate/3, more_faulty/2, less_faulty/2, no_faults/1,
prop_shrinks_without_duplicates/1, shrink_without_duplicates/1,
is_generator/1]).
-import(eqc_symbolic,
[eval/1,eval/2,defined/1,well_defined/1,pretty_print/1,pretty_print/2]).
-import(eqc,[equals/2,
fails/1,
conjunction/1,
collect/2,collect/3,classify/3,aggregate/2,aggregate/3,measure/3,
%distribution/0,
with_title/1,
%print_distribution/1,
numtests/2,
on_output/2,
on_test/2,
quickcheck/1,
counterexample/0,counterexample/1,
current_counterexample/0,
module/1,
check/2,
recheck/1]).
-compile({parse_transform,eqc_warn}).

1
lib/erlang_js Submodule

@ -0,0 +1 @@
Subproject commit 709b568efbc99c954507d1593bc5633f900bc5dc

1
lib/erlv8 Submodule

@ -0,0 +1 @@
Subproject commit 7443cc8e2e06f7907a9e1e44c181255e188cfb97

Binary file not shown.

View file

@ -1,50 +0,0 @@
%%%%----------------------------------------------------
%%% @author Mattias Pettersson <mattiaspgames@gmail.com>
%%% @copyright 2011 Mattias Pettersson
%%% @doc Database for runtime game variable storage.
%%% @end
Test Mnesia
-module(gamedb).
-import(mnesia).
-export([init/0,insert_player/1,example_player/0,read_player/1,test_player/0]).
-include("gamedb.hrl").
%%-----------------------------------------------------
%% Creation
%%-----------------------------------------------------
init() ->
mnesia:create_table(player, [{attributes, record_info(fields, player)}]).
%%-----------------------------------------------------
%% Test
%%-----------------------------------------------------
test_player() ->
insert_player(example_player()),
read_player(0001).
example_player() ->
#player{id = 0001,
name = "Tux"}.
%%-----------------------------------------------------
%% Insertions
%%-----------------------------------------------------
insert_player(Player) ->
Fun = fun() ->
mnesia:write(Player)
end,
mnesia:transaction(Fun).
%%-----------------------------------------------------
%% Querries
%%-----------------------------------------------------
read_player(Player_Key) ->
Fun = fun() ->
[P] = mnesia:read(player, Player_Key),
Name = P#player.name,
io:format("Player name: ~s~n",[Name])
end,
mnesia:transaction(Fun).

View file

@ -1,6 +0,0 @@
%% gamedb.hrl
-record(player, {id, name}).

View file

@ -1,12 +0,0 @@
1. From terminal: erl -mnesia dir '"/home/user/dir/to/GGS/GameDB.Player"'
2. mnesia:create_schema([node()]).
3. mnesia:start().
4. c(gamedb).
5. gamedb:init().
6. mnesia:info().
7. gamedb:test_player().
Last output should be:
Player name: Tux
{atomic,ok}

View file

@ -1,22 +1,84 @@
#!/usr/bin/env python
import sys
import socket
import sys, time, socket
HOST = 'localhost' # The remote host
PORT = int(sys.argv[1]) # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send('__hello 0')
fs = s.makefile()
data = fs.readline()
token = data.split(" ")[0]
print "Token:", token
s.send(token+" __echo 0 msg")
data = s.recv(1024)
print data
# Define ourselves a function!
token = s.recv(1024)
#print "Defining a function called myFun"
#s.send(
#"Token: %s\n\
#Server-Command: define\n\
#Content-Type: text\n\
#Content-Length: 49\n\
#\n\
#function myFun() {return 'Hello World!' ;}" % token)
#fs = s.makefile()
#data = fs.readline()
#print "Token:", token
#print "Data: ", ' '.join(data.split(" ")[1:])
# Call that function!
fs = s.makefile()
print "Token: ", token
s.send(
"Token: %s\n\
Game-Command: greet\n\
Content-Type: text\n\
Content-Length: 0\n\
\n\
" % token)
time.sleep(1)
s.send(
"Token: %s\n\
Game-Command: uname\n\
Content-Type: text\n\
Content-Length: 0\n\
\n\
" % token)
time.sleep(1)
s.send(
"Token: %s\n\
Game-Command: chat\n\
Content-Type: text\n\
Content-Length: 23\n\
\n\
Hello guys, what's up?\n" % token)
time.sleep(3)
while True:
data = fs.readline()
print "Data: ", data
s.close()
time.sleep(2)
HOST = 'localhost' # The remote host
PORT = int(sys.argv[1]) # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
# Call that function!
print "Calling myFun"
s.send(
"Token: %s\n\
Server-Command: call\n\
Content-Type: text\n\
Content-Length: 6\n\
\n\
myFun" % token)
fs = s.makefile()
data = fs.readline()
print "Token:", token
print "Data: ", ' '.join(data.split(" ")[1:])
s.close()

23
python_client_reconnect Executable file
View file

@ -0,0 +1,23 @@
#!/usr/bin/env python
import sys, time, socket
HOST = 'localhost' # The remote host
PORT = int(sys.argv[1]) # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
# Call that function!
print "Calling myFun"
s.send(
"Token: %s\n\
Server-Command: call\n\
Content-Type: text\n\
Content-Length: 6\n\
\n\
myFun" % raw_input("Token >> "))
fs = s.makefile()
data = fs.readline()
print "Data: ", ' '.join(data.split(" ")[1:])
s.close()

Binary file not shown.

177
src/ggs_api.js Normal file
View file

@ -0,0 +1,177 @@
function Storage(type) {
if (type == "world" || type == "localStorage" || type == "players") {
this.type = type;
var self = this;
return {
setItem: function(key, value) {
if(this.type != "players")
callErlang("ggs_db setItem " + escapeErlang([GGS.tableToken, self.type, key, value]));
else
throw "No such method setItem()";
},
getItem: function(key) {
return callErlang("ggs_db getItem " + escapeErlang([GGS.tableToken, self.type, key]));
},
key: function(position) {
return callErlang("ggs_db key " + escapeErlang([GGS.tableToken, self.type, position]));
},
length: {
get: function() {
return callErlang("ggs_db length " + escapeErlang([GGS.tableToken, self.type]));
}
},
removeItem: function(key) {
if(this.type != "players")
callErlang("ggs_db removeItem " + escapeErlang([GGS.tableToken, self.type, key]));
else
throw "No such method removeItem()";
},
clear: function() {
if(this.type != "players")
callErlang("ggs_db clear " + escapeErlang([GGS.tableToken, self.type]));
else
throw "No such method clear()";
}
}
} else throw "GGS: No such storage available " + type;
}
function _GGS(tableToken) {
this.tableToken = tableToken;
var world = new Storage("world");
this.__defineGetter__("world", function() {
return world;
});
var localStorage = new Storage("localStorage");
this.__defineGetter__("localStorage", function() {
return localStorage;
});
var players = new Storage("players");
this.__defineGetter__("players", function() {
return players;
});
var tableToken = this.tableToken;
this.__defineGetter__("tableToken", function() {
return tableToken;
});
}
_GGS.prototype.sendCommandToAll = function(command, args) {
var message = "{" + command + "," + args + "}";
callErlang("ggs_table send_command_to_all " + escapeErlang([this.tableToken, message]));
}
_GGS.prototype.serverLog = function(message) {
callErlang("'error_logger info_msg " + escapeErlang([message]) + "'");
}
function escapeErlang(args) {
var str = JSON.stringify(args);
str = str.replace("'", "\\\'");
return str;
}
function Player(token) {
var playerToken = token;
this.__defineGetter__("id", function() {
return playerToken;
});
return {
sendCommand: function(command, args) {
ejsLog("/tmp/ggs-test.txt", "'ggs_table send_command " + escapeErlang([GGS.tableToken+ "", playerToken, command, args])+"'");
//callErlang("'ggs_table send_command " + escapeErlang([GGS.tableToken+ "", playerToken, command, args]) + "'");
ejsLog("/tmp/ggs-test.txt", "done");
}
}
}
/*
// ------------ Player stuff -------------
// TODO: remove this later on
function playerCommand(player, command, args) {
switch(command) {
case "greet":
player.sendCommand("notification", "Welcome on our server!");
var new_nick = args;
if(validNick(new_nick)) {
newNick(new_nick);
GGS.sendCommandToAll("joined", new_nick);
}
break;
case "chat":
GGS.sendCommandToAll("chat", args);
break;
case "uname":
player.sendCommand("notice", callErlang("os cmd [\"uname -a\"]"))
break;
case "lplayers":
listUsers(player);
break;
case "nick":
if(validNick(new_nick)) {
newNick(new_nick);
GGS.sendCommandToAll("nickchange", old_nick + "," + nicks[player.id]);
}
break;
default:
player.sendCommand("error", "Command not found");
break;
}
}
function validNick(new_nick) {
if(new_nick.lastIndexOf(",") != -1) {
player.sendCommand("error", "Mallformed nick " + new_nick);
return false;
}
var nicks_s = GGS.localStorage("nicks");
var nicks = {};
if(nicks_s != "") { // if not the first player
nicks = JSON.parse(nicks_s);
}
for (var id in nicks) {
if (nicks[id] == new_nick) {
player.sendCommand("error", "Nick " + new_nick + " is already taken");
return false;
}
}
return true;
}
function newNick(new_nick) {
var nicks_s = GGS.localStorage("nicks");
var nicks = {};
if(nicks_s != "") { // if not the first player
nicks = JSON.parse(nicks_s);
}
nicks[player.id] = new_nick;
old_nick = nicks[player.id];
GGS.localStorage.setItem("nicks", JSON.stringify(nicks));
}
function listUsers(player) {
var nicks = JSON.parse(GGS.localStorage.getItem("nicks"));
var nicks_a = [];
for(var id in nicks) {
nicks_a.push(nicks[id])
}
player.sendCommand("nicklist", nicks_a.join(","));
}
*/

Some files were not shown because too many files have changed in this diff Show more