diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8cb3c6082d8004c30a687f1214c70ec21b730bb5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,201 @@ +cmake_minimum_required(VERSION 2.8.3) +project(socrates_workshop) + +## Compile as C++11, supported in ROS Kinetic and newer +# add_compile_options(-std=c++11) + +## Find catkin macros and libraries +## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) +## is used, also find other catkin packages +find_package(catkin REQUIRED COMPONENTS + roscpp + rospy + std_msgs + message_generation +) + +## System dependencies are found with CMake's conventions +# find_package(Boost REQUIRED COMPONENTS system) + + +## Uncomment this if the package has a setup.py. This macro ensures +## modules and global scripts declared therein get installed +## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html +# catkin_python_setup() + +################################################ +## Declare ROS messages, services and actions ## +################################################ + +## To declare and build messages, services or actions from within this +## package, follow these steps: +## * Let MSG_DEP_SET be the set of packages whose message types you use in +## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...). +## * In the file package.xml: +## * add a build_depend tag for "message_generation" +## * add a build_depend and a run_depend tag for each package in MSG_DEP_SET +## * If MSG_DEP_SET isn't empty the following dependency has been pulled in +## but can be declared for certainty nonetheless: +## * add a run_depend tag for "message_runtime" +## * In this file (CMakeLists.txt): +## * add "message_generation" and every package in MSG_DEP_SET to +## find_package(catkin REQUIRED COMPONENTS ...) +## * add "message_runtime" and every package in MSG_DEP_SET to +## catkin_package(CATKIN_DEPENDS ...) +## * uncomment the add_*_files sections below as needed +## and list every .msg/.srv/.action file to be processed +## * uncomment the generate_messages entry below +## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...) + +## Generate messages in the 'msg' folder +add_message_files( +# FILES +# Message1.msg +# Message2.msg +) + +## Generate services in the 'srv' folder +add_service_files( + FILES + MiniMax.srv + IntInt.srv + Board.srv +) + +## Generate actions in the 'action' folder +# add_action_files( +# FILES +# Action1.action +# Action2.action +# ) + +## Generate added messages and services with any dependencies listed here +generate_messages( + DEPENDENCIES + std_msgs +) + +################################################ +## Declare ROS dynamic reconfigure parameters ## +################################################ + +## To declare and build dynamic reconfigure parameters within this +## package, follow these steps: +## * In the file package.xml: +## * add a build_depend and a run_depend tag for "dynamic_reconfigure" +## * In this file (CMakeLists.txt): +## * add "dynamic_reconfigure" to +## find_package(catkin REQUIRED COMPONENTS ...) +## * uncomment the "generate_dynamic_reconfigure_options" section below +## and list every .cfg file to be processed + +## Generate dynamic reconfigure parameters in the 'cfg' folder +# generate_dynamic_reconfigure_options( +# cfg/DynReconf1.cfg +# cfg/DynReconf2.cfg +# ) + +################################### +## catkin specific configuration ## +################################### +## The catkin_package macro generates cmake config files for your package +## Declare things to be passed to dependent projects +## INCLUDE_DIRS: uncomment this if you package contains header files +## LIBRARIES: libraries you create in this project that dependent projects also need +## CATKIN_DEPENDS: catkin_packages dependent projects also need +## DEPENDS: system dependencies of this project that dependent projects also need +catkin_package( +# INCLUDE_DIRS include +# LIBRARIES socrates_workshop + CATKIN_DEPENDS roscpp rospy std_msgs message_runtime +# DEPENDS system_lib +) + +########### +## Build ## +########### + +## Specify additional locations of header files +## Your package locations should be listed before other locations +include_directories( +# include + ${catkin_INCLUDE_DIRS} +) + +## Declare a C++ library +# add_library(${PROJECT_NAME} +# src/${PROJECT_NAME}/socrates_workshop.cpp +# ) + +## Add cmake target dependencies of the library +## as an example, code may need to be generated before libraries +## either from message generation or dynamic reconfigure +# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) + +## Declare a C++ executable +## With catkin_make all packages are built within a single CMake context +## The recommended prefix ensures that target names across packages don't collide +# add_executable(${PROJECT_NAME}_node src/socrates_workshop_node.cpp) + +## Rename C++ executable without prefix +## The above recommended prefix causes long target names, the following renames the +## target back to the shorter version for ease of user use +## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node" +# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "") + +## Add cmake target dependencies of the executable +## same as for the library above +# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) + +## Specify libraries to link a library or executable target against +# target_link_libraries(${PROJECT_NAME}_node +# ${catkin_LIBRARIES} +# ) + +############# +## Install ## +############# + +# all install targets should use catkin DESTINATION variables +# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html + +## Mark executable scripts (Python etc.) for installation +## in contrast to setup.py, you can choose the destination +# install(PROGRAMS +# scripts/my_python_script +# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +# ) + +## Mark executables and/or libraries for installation +# install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node +# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} +# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} +# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +# ) + +## Mark cpp header files for installation +# install(DIRECTORY include/${PROJECT_NAME}/ +# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} +# FILES_MATCHING PATTERN "*.h" +# PATTERN ".svn" EXCLUDE +# ) + +## Mark other files for installation (e.g. launch and bag files, etc.) +# install(FILES +# # myfile1 +# # myfile2 +# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} +# ) + +############# +## Testing ## +############# + +## Add gtest based cpp test target and link libraries +# catkin_add_gtest(${PROJECT_NAME}-test test/test_socrates_workshop.cpp) +# if(TARGET ${PROJECT_NAME}-test) +# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}) +# endif() + +## Add folders to be run by python nosetests +# catkin_add_nosetests(test) diff --git a/launch/tic_tac_toe.launch b/launch/tic_tac_toe.launch new file mode 100644 index 0000000000000000000000000000000000000000..a80850177a54289be7dcb53b5cd7c7ac5e2ad39d --- /dev/null +++ b/launch/tic_tac_toe.launch @@ -0,0 +1,27 @@ +<launch> + <param name="application_name" value="tic_tac_toe" type="string" /> + <param name="topic_minimax_service" value="call_minimax" type="string" /> + <param name="topic_perform_robot_action" value="perform_action" type="string" /> + <param name="topic_difficulty" value="set_difficulty" type="string" /> + <param name="topic_ask_help" value="help_user" type="string" /> + <param name="topic_board_state" value="board_state" type="string" /> + <param name="topic_gripper_callback" value="gripper_action_callback" type="string" /> + <param name="topic_gripper_action" value="gripper_action" type="string" /> + + <param name="param_difficulty" value="1" type="int" /> + <param name="param_difficulty_min" value="1" type="int" /> + <param name="param_difficulty_max" value="4" type="int" /> + + <param name="param_human_player_id" value="1" type="int" /> + <param name="param_ai_player_id" value="-1" type="int" /> + + <node name="node_minimax_server" pkg="socrates_workshop" type="node_minimax.py" /> + <node name="node_main" pkg="socrates_workshop" type="node_main.py" /> + <node name="node_difficulty" pkg="socrates_workshop" type="node_difficulty.py" /> + <node name="node_human_helper" pkg="socrates_workshop" type="node_human_helper.py" /> +</launch> + + +<!-- + <param name="" value="" type="" /> +--> diff --git a/package.xml b/package.xml new file mode 100644 index 0000000000000000000000000000000000000000..980d2d6049df28dfd1917856dbb911099601064f --- /dev/null +++ b/package.xml @@ -0,0 +1,66 @@ +<?xml version="1.0"?> +<package> + <name>socrates_workshop</name> + <version>0.0.2</version> + <description>The socrates_workshop package</description> + + <!-- One maintainer tag required, multiple allowed, one person per tag --> + <!-- Example: --> + <!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> --> + <maintainer email="michelep@cs.umu.se">michele</maintainer> + + + <!-- One license tag required, multiple allowed, one license per tag --> + <!-- Commonly used license strings: --> + <!-- BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 --> + <license>TODO</license> + + + <!-- Url tags are optional, but multiple are allowed, one per tag --> + <!-- Optional attribute type can be: website, bugtracker, or repository --> + <!-- Example: --> + <!-- <url type="website">http://wiki.ros.org/socrates_workshop</url> --> + + + <!-- Author tags are optional, multiple are allowed, one per tag --> + <!-- Authors do not have to be maintainers, but could be --> + <!-- Example: --> + <!-- <author email="jane.doe@example.com">Jane Doe</author> --> + + + <!-- The *_depend tags are used to specify dependencies --> + <!-- Dependencies can be catkin packages or system dependencies --> + <!-- Examples: --> + <!-- Use build_depend for packages you need at compile time: --> + <!-- <build_depend>message_generation</build_depend> --> + <!-- Use buildtool_depend for build tool packages: --> + <!-- <buildtool_depend>catkin</buildtool_depend> --> + <!-- Use run_depend for packages you need at runtime: --> + <!-- <run_depend>message_runtime</run_depend> --> + <!-- Use test_depend for packages you need only for testing: --> + <!-- <test_depend>gtest</test_depend> --> + <buildtool_depend>catkin</buildtool_depend> + <build_depend>roscpp</build_depend> + <build_depend>rospy</build_depend> + <build_depend>std_msgs</build_depend> + <build_depend>message_generation</build_depend> + + <build_depend>sensor_msgs</build_depend> + <build_depend>opencv2</build_depend> + <build_depend>cv_bridge</build_depend> + + <run_depend>roscpp</run_depend> + <run_depend>rospy</run_depend> + <run_depend>std_msgs</run_depend> + <run_depend>message_runtime</run_depend> + + <run_depend>sensor_msgs</run_depend> + <run_depend>opencv2</run_depend> + <run_depend>cv_bridge</run_depend> + + <!-- The export tag contains other, unspecified, tags --> + <export> + <!-- Other tools can request additional information be placed here --> + + </export> +</package> diff --git a/scripts/node_difficulty.py b/scripts/node_difficulty.py new file mode 100755 index 0000000000000000000000000000000000000000..a59f7eb23e0500166b733a1f75993ec4d5eacbe7 --- /dev/null +++ b/scripts/node_difficulty.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +import rospy +import tic_tac_toe_common as ttt + +from std_msgs.msg import Int16 +from socrates_workshop.srv import * + +NODE_NAME = "node_difficulty" + + +PARAM_DIFFICULTY = "param_difficulty" +PARAM_DIFFICULTY_MIN = "param_difficulty_min" +PARAM_DIFFICULTY_MAX = "param_difficulty_max" + +TOPIC_DIFFICULTY = "topic_difficulty" + + +def setCurrentDifficulty(value): + + min_diff = rospy.get_param(PARAM_DIFFICULTY_MIN) + max_diff = rospy.get_param(PARAM_DIFFICULTY_MAX) + + value = max(min_diff, min(value, max_diff)) + + rospy.set_param(PARAM_DIFFICULTY, value) + return value + +def difficultyCallback(msg): + value = msg.data + + value = setCurrentDifficulty(value) + + return IntIntResponse(value) + +def init_this_node(): + + rospy.init_node(NODE_NAME, anonymous=True) + + + s = rospy.Service(ttt.concatTopicName(TOPIC_DIFFICULTY), IntInt, difficultyCallback) + + + rospy.spin() + +if __name__ == '__main__': + init_this_node() diff --git a/scripts/node_human_helper.py b/scripts/node_human_helper.py new file mode 100755 index 0000000000000000000000000000000000000000..81e612ce3d954fc1039c8c2926f9668d32a63dcd --- /dev/null +++ b/scripts/node_human_helper.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +import rospy +import tic_tac_toe_common as ttt +from std_msgs.msg import * + +from socrates_workshop.srv import MiniMax, Board, IntInt + +NODE_NAME = "node_main" + + +TOPIC_ASK_HELP = "topic_ask_help" +TOPIC_BOARD_STATE = "topic_board_state" +TOPIC_MINIMAX = "topic_minimax_service" +TOPIC_DIFFICULTY = "topic_difficulty" + +TOPIC_ACTION_CALLBACK = "topic_gripper_callback" + + +this = sys.modules[__name__] + +this.board_service_proxy = None +this.minimax_service_proxy = None +this.gripper_subscriber = None +this.difficulty_service_proxy = None + + + +def helpUser(nextAction, board): + rospy.loginfo("I suggest action %s for board %s", nextAction, board) + +def helpCallback(msg): + id_human = ttt.getUserId() + id_ai = ttt.getAiId() + + msg_board = this.board_service_proxy(id_human, id_ai) + + depth = 10 + msg_action = this.minimax_service_proxy(msg_board.board, depth, id_human, id_ai) + + helpUser(msg_action.action, msg_board.board) + + +""" +Callback function for the gripper movement +Depending of current difficulty we execute different routines +""" +def callbackGripperActionExecuted(msg): + success = msg.data + + # We query the current difficulty level + msg_difficulty = this.difficulty_service_proxy(0) + difficulty = msg_difficulty.data + + if difficulty == 1: + helpCallback(None) + + if difficulty == 4: + # countdown + pass + + +def init_this_node(): + + rospy.init_node(NODE_NAME, anonymous=True) + + topic = ttt.concatTopicName(TOPIC_ASK_HELP) + s = rospy.Subscriber(topic, Empty, helpCallback) + + topic = ttt.concatTopicName(TOPIC_BOARD_STATE) + this.board_service_proxy = rospy.ServiceProxy(topic, Board) + + topic = ttt.concatTopicName(TOPIC_MINIMAX) + this.minimax_service_proxy = rospy.ServiceProxy(topic, MiniMax) + + + + # Difficulty service to query the current level of difficulty + topic = ttt.concatTopicName(TOPIC_DIFFICULTY) + this.difficulty_service_proxy = rospy.ServiceProxy(topic, IntInt) + + # Subscriber on the gripper callback invoked each time the gipper concludes an action + topic = ttt.concatTopicName(TOPIC_ACTION_CALLBACK) + this.gripper_subscriber = rospy.Subscriber(topic, Bool, callbackGripperActionExecuted) + + + rospy.spin() + +if __name__ == '__main__': + init_this_node() diff --git a/scripts/node_main.py b/scripts/node_main.py new file mode 100755 index 0000000000000000000000000000000000000000..0ee7ecfbb5643ca9361dcb9183acbda84a3abcb6 --- /dev/null +++ b/scripts/node_main.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python +import rospy +import tic_tac_toe_common as ttt +from std_msgs.msg import * +from std_srvs.srv import * +from socrates_workshop.srv import * + +NODE_NAME = "node_main" + + +TOPIC_BOARD_STATE = "topic_board_state" +TOPIC_PERFORM_ACTION = "topic_perform_robot_action" +TOPIC_MINIMAX = "topic_minimax_service" + +TOPIC_ACTION_CALLBACK = "topic_gripper_callback" +TOPIC_GRIPPER_ACTION = "topic_gripper_action" + + +this = sys.modules[__name__] + +# Services +this.action_service = None + +# Service proxies +this.board_service_proxy = None +this.minimax_service_proxy = None + +this.gripper_action_publisher = None + +# Other variables +this.gripper_moving = False + + +""" +Helper function to call the minimax service +""" +def callMinimax(): + id_h = ttt.getUserId() + id_ai = ttt.getAiId() + + resp = this.board_service_proxy(id_h,id_ai) + current_state = resp.board + + depth = 10 + resp = this.minimax_service_proxy(current_state, depth, id_ai, id_h) + + return resp.action + + +""" +Callback function for the gripper movement +""" +def callbackGripperActionExecuted(msg): + success = msg.data + + # The gripper stopped moving + this.gripper_moving = False + + +""" +Invoked each time service on TOPIC_PERFORM_ACTION is called + +""" +def actionCallback(msg): + + # If gripper is still performing the previous action + # we do nothing + if this.gripper_moving: + pass + + else: + # We set the gripper as moving + this.gripper_moving = True + + # We find the correct action to perform + action = callMinimax() + + # The action id (between 1 and 9) is published + # for the gripper node + this.gripper_action_publisher.publish(action) + + return EmptyResponse() + + +def init_this_node(): + + rospy.init_node(NODE_NAME, anonymous=True) + + # Service that provides the action execution + topic = ttt.concatTopicName(TOPIC_PERFORM_ACTION) + this.action_service = rospy.Service(topic, Empty, actionCallback) + + # Board service to query the state of the board + topic = ttt.concatTopicName(TOPIC_BOARD_STATE) + this.board_service_proxy = rospy.ServiceProxy(topic, Board) + + # Minimax service + topic = ttt.concatTopicName(TOPIC_MINIMAX) + this.minimax_service_proxy = rospy.ServiceProxy(topic, MiniMax) + + # We will publish on the gripper topic the action to execute (actions have ids 1 to 9) + topic = ttt.concatTopicName(TOPIC_GRIPPER_ACTION) + this.gripper_action_publisher = rospy.Publisher(topic, Int16, queue_size=10) + + + # Subscriber on the gripper callback invoked each time the gipper concludes an action + topic = ttt.concatTopicName(TOPIC_ACTION_CALLBACK) + this.gripper_subscriber = rospy.Subscriber(topic, Bool, callbackGripperActionExecuted) + + rospy.spin() + +if __name__ == '__main__': + init_this_node() diff --git a/scripts/node_minimax.py b/scripts/node_minimax.py new file mode 100755 index 0000000000000000000000000000000000000000..49b8aa5c64e8bfae92b7348100b17046d1e0121c --- /dev/null +++ b/scripts/node_minimax.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python +import rospy +import tic_tac_toe_common as ttt + +from socrates_workshop.srv import * + +Inf = +999999 + +NODE_NAME = 'minimax_server' + + +TOPIC_MINIMAX = "topic_minimax_service" + + + +def h1(s, player, indeces): + return all([s[i] == player for i in indeces]) + +def h2(s,p): + return h1(s,p,[0,1,2]) or h1(s,p,[3,4,5]) or h1(s,p,[6,7,8]) or h1(s,p,[0,3,6]) or h1(s,p,[1,4,7]) or h1(s,p,[2,5,8]) or h1(s,p,[0,4,8]) or h1(s,p,[2,4,6]) + + +def getOpponent(player, p1, p2): + if player == p1: + return p2 + if player == p2: + return p1 + return 0 + +def getHeuristicValue(s, player, p1, p2): + opp = getOpponent(player, p1, p2) + return 10*h2(s, player) + sum(1 for x in range(len(s)) if s[x] == 0) - 10 * h2(s, opp) + + +def isTerminal(s, player, p1, p2): + opp = getOpponent(player, p1, p2) + return all([cell != 0 for cell in s]) or h2(s, player) or h2(s, opp) + +def possibleActions(s, player): + return [x+1 for x in range(len(s)) if s[x] == 0] + + +def executeAction(s, action, player): + s[action-1] = player + return s + +def minimax(state, player, p1, p2, depth): + selectedAction = None + opp = getOpponent(player, p1, p2) + + if isTerminal(state, player, p1, p2) or depth == 0: + h = getHeuristicValue(state, p1, p1, p2) + return [h, selectedAction] + + if player == p1: + alpha = -Inf + for action in possibleActions(state, player): + s1 = executeAction([x for x in state], action, player) + childScore = minimax(s1, opp, p1, p2, depth - 1)[0] + if childScore > alpha: + alpha = childScore + selectedAction = action + else: + alpha = Inf + for action in possibleActions(state, player): + s1 = executeAction([x for x in state], action, player) + childScore = minimax(s1, opp, p1, p2, depth - 1)[0] + if childScore < alpha: + alpha = childScore + selectedAction = action + + return [alpha, selectedAction] + +def handleComputeMiniMax(req): + cells = [x for x in req.board] + depth = req.max_depth + p1 = req.id_player + p2 = req.id_opponent + + alpha, selectedAction = minimax(cells, p1, p1, p2, depth) + + return MiniMaxResponse(selectedAction) + +def minimaxServer(): + rospy.init_node(NODE_NAME) + + s = rospy.Service(ttt.concatTopicName(TOPIC_MINIMAX), MiniMax, handleComputeMiniMax) + rospy.spin() + +if __name__ == "__main__": + minimaxServer() diff --git a/scripts/tic_tac_toe.py b/scripts/tic_tac_toe.py new file mode 100755 index 0000000000000000000000000000000000000000..82b486ab0798e2ef385c59e6cdc21f2a9994bde0 --- /dev/null +++ b/scripts/tic_tac_toe.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python + +import rospy +import os +import tic_tac_toe_common as ttt + +from socrates_workshop.srv import * +from std_msgs.msg import Bool, Int16 +from std_srvs.srv import Empty + + + +TOPIC_BOARD_STATE = "topic_board_state" +TOPIC_PERFORM_ACTION = "topic_perform_robot_action" + +TOPIC_GRIPPER_ACTION = "topic_gripper_action" +TOPIC_ACTION_CALLBACK = "topic_gripper_callback" + +clearTerminal = lambda: os.system('clear') + +this = sys.modules[__name__] + +this.callback_publisher = None +this.robot_action_proxy = None + +this.refreshing = False +this.board = [0,0,0,0,0,0,0,0,0] +this.last_move = 0 + +def h1(s, player, indeces): + return all([s[i] == player for i in indeces]) + +def h2(s,p): + return h1(s,p,[0,1,2]) or h1(s,p,[3,4,5]) or h1(s,p,[6,7,8]) or h1(s,p,[0,3,6]) or h1(s,p,[1,4,7]) or h1(s,p,[2,5,8]) or h1(s,p,[0,4,8]) or h1(s,p,[2,4,6]) + +def gameFinished(): + + id_human = ttt.getUserId() + id_ai = ttt.getAiId() + indeces = [0,1,2,3,4,5,6,7,8] + + s = this.board + + if h2(s, id_human) or h2(s, id_ai) or all( [s[i] != 0 for i in indeces] ): + return True + else: + return False + + +def newGame(): + this.board = [0,0,0,0,0,0,0,0,0] + this.last_move = 0 + return this.board + +def getPiece(p): + if p == 0: + return ' ' + elif p == 1: + return 'X' + else: + return 'O' + + +def refreshView(): + if not this.refreshing: + this.refreshing = True + s = this.board + clearTerminal() + print "let's play tic tac toe!!" + print " %s | %s | %s" % (getPiece(s[0]), getPiece(s[1]), getPiece(s[2])) + print "-----------" + print " %s | %s | %s" % (getPiece(s[3]), getPiece(s[4]), getPiece(s[5])) + print "-----------" + print " %s | %s | %s" % (getPiece(s[6]), getPiece(s[7]), getPiece(s[8])) + print "Enter your desired piece position in range [1..9]" + this.refreshing = False + + +def setPiece(player, position): + if this.board[position-1] != 0: + return False + this.board[position-1] = player + this.last_move = player + if gameFinished(): + newGame() + + refreshView() + return True + +def callbackBoardRequest(req): + return BoardResponse(this.board) + +def callbackOnGripperAction(msg): + action = msg.data + setPiece(ttt.getAiId(), action) + this.callback_publisher.publish(True) + + +def tic_tac_toe_node(): + rospy.init_node("tic_tac_toe") + + + id_human = ttt.getUserId() + + exit = False + + topic = ttt.concatTopicName(TOPIC_BOARD_STATE) + s = rospy.Service(topic, Board, callbackBoardRequest) + + topic = ttt.concatTopicName(TOPIC_GRIPPER_ACTION) + s = rospy.Subscriber(topic, Int16, callbackOnGripperAction) + + topic = ttt.concatTopicName(TOPIC_ACTION_CALLBACK) + this.callback_publisher = rospy.Publisher(topic, Bool, queue_size=10) + + topic = ttt.concatTopicName(TOPIC_PERFORM_ACTION) + this.robot_action_proxy = rospy.ServiceProxy(topic, Empty) + + + clearTerminal() + newGame() + while not rospy.is_shutdown() and not exit: + refreshView() + while this.last_move == id_human: + pass + user_input = input() + + if user_input not in range(1, 10): + exit = True + + if setPiece(id_human, user_input): + this.robot_action_proxy() + + print exit + + +if __name__ == "__main__": + tic_tac_toe_node() diff --git a/scripts/tic_tac_toe_common.py b/scripts/tic_tac_toe_common.py new file mode 100644 index 0000000000000000000000000000000000000000..d00d7b6fe2b8215c925a460e70d67d05374671b8 --- /dev/null +++ b/scripts/tic_tac_toe_common.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +import rospy + +from std_srvs.srv import Empty + +PARAM_APPLICATION_NAME = "application_name" + + +PARAM_USER_ID = "param_human_player_id" +PARAM_AI_ID = "param_ai_player_id" + + +def getApplicationName(): + return rospy.get_param(PARAM_APPLICATION_NAME) + + +def concatTopicName(topicNameParam): + application_name = getApplicationName() + + topic_name = rospy.get_param(topicNameParam) + topic_name = application_name + "/" + topic_name + + return topic_name + +def getUserId(): + return rospy.get_param(PARAM_USER_ID) + +def getAiId(): + return rospy.get_param(PARAM_AI_ID) + diff --git a/scripts/tic_tac_toe_common.pyc b/scripts/tic_tac_toe_common.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8972205a838900ad9964aa1da348081568d95798 Binary files /dev/null and b/scripts/tic_tac_toe_common.pyc differ diff --git a/srv/Board.srv b/srv/Board.srv new file mode 100644 index 0000000000000000000000000000000000000000..e28698101ccc759deee12d2c64c4fb2d3b07c1aa --- /dev/null +++ b/srv/Board.srv @@ -0,0 +1,4 @@ +int32 id_human +int32 id_ai +--- +int32[] board diff --git a/srv/IntInt.srv b/srv/IntInt.srv new file mode 100644 index 0000000000000000000000000000000000000000..71badf76326020cf84e15257e43f3fb7063518ae --- /dev/null +++ b/srv/IntInt.srv @@ -0,0 +1,3 @@ +int32 data +--- +int32 data diff --git a/srv/MiniMax.srv b/srv/MiniMax.srv new file mode 100644 index 0000000000000000000000000000000000000000..1f63c70bff754bb2c120baa8067592d637b1e9c2 --- /dev/null +++ b/srv/MiniMax.srv @@ -0,0 +1,6 @@ +int32[] board +int32 max_depth +int32 id_player +int32 id_opponent +--- +int32 action