Мультиплеерный морской бой на sh


/ Published in: Bash
Save to your folder(s)

Делаем исполняемым cd ~ && chmod +x ship.sh
Для запуска сервера: ./ship.sh -serve
Для подключения к серверу ./ship.sh host


Copy this code and paste it in your HTML
  1. #!/bin/bash
  2. # sink.sh - a networked battleship game for Bourne-again shell.
  3. #
  4. # Copyright Gregory Bonik, 2009
  5. # gregory@bonik.org
  6. #
  7. # Beerware licence is well applicable.
  8.  
  9.  
  10. # All external stuff we use. Change if needed.
  11. NETCAT=nc
  12. STTY=stty
  13. CLEAR=clear
  14.  
  15.  
  16. ##############################################################################
  17. # Let there be code
  18. ##############################################################################
  19.  
  20. VERSION="0.0.1a"
  21.  
  22. CHAR_SHIP='@'
  23. CHAR_ACTIVE='%'
  24. CHAR_DEAD='X'
  25. CHAR_WATER='~'
  26. CHAR_MISS='o'
  27. CHAR_CROSSHAIR='*'
  28.  
  29. EXENAME="$0"
  30.  
  31. quit() {
  32. read -t 0.5 -n 20
  33. $STTY echo
  34. exit $1
  35. }
  36.  
  37. usage() {
  38. echo "Sink'em all - a networked battleship game for Bourne-again shell."
  39. echo "Version $VERSION"
  40. echo "Usage: $EXENAME [ -serve | HOST ] [ -p PORT ]"
  41. quit
  42. }
  43.  
  44. title() {
  45. $CLEAR
  46. local SPACE=" "
  47. local MSG1_XXXXXXXXXXXXXXXXXXXXXXXXXXX="$MSG1${SPACE:0:$((33 - ${#MSG1}))}"
  48. local MSG2_XXXXXXXXXXXXXXXXXXXXXXXXXXX="$MSG2${SPACE:0:$((33 - ${#MSG2}))}"
  49. local MSG3_XXXXXXXXXXXXXXXXXXXXXXX="$MSG3${SPACE:0:$((29 - ${#MSG3}))}"
  50. local MSG4_XXXXXXXXXXXXXXXXXXXXXXX="$MSG4${SPACE:0:$((29 - ${#MSG4}))}"
  51. echo "O-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..O"
  52. echo ") .- . | . , _ .'| | | | ("
  53. echo ") \`; | |\\| |\`. (_' |\`| :-: | | . ("
  54. echo ') .-. _`_ _ _ _ _________________`_`_`_`_ _ .-. ('
  55. echo ") \`.' \`--' ,__ \`--' '.' ("
  56. echo ") ;__{ ("
  57. echo ") $MSG1_XXXXXXXXXXXXXXXXXXXXXXXXXXX ; ("
  58. echo ") $MSG2_XXXXXXXXXXXXXXXXXXXXXXXXXXX |.------. ("
  59. echo ") ;-----| | ("
  60. echo ") $MSG3_XXXXXXXXXXXXXXXXXXXXXXX | \`------\` ("
  61. echo ") $MSG4_XXXXXXXXXXXXXXXXXXXXXXX \`---.------\\--- ("
  62. echo ') ;-----| \\ | ('
  63. echo ') / | \ \ | ('
  64. echo ') / ;_______\ \ ('
  65. echo ') ------.--------\ \ ('
  66. echo ') / / / ; \ \ ('
  67. echo ') / / / / ; \/ ('
  68. echo ') / / / / /; .------/ ('
  69. echo ") /-/-/-/-/-\`'_____//_, ("
  70. echo ") / # } ("
  71. echo ") / o o o o o o # | (C) 2009 G. B. ("
  72. echo ") ~ ~~..~~.. ..~~~.. ~~. \`~~~~~....._____#_-' ~~.. ~~.. ..~~~... ~~.. ~~.. ~ ("
  73. echo ") ~~ ..~~~.. ..~... ..~~~. ~~..~~ ~~..~~ ~~..~~ ~~..~~..~~..~ ~~ ~~ ~ ("
  74. echo -n "O-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..O"
  75. }
  76.  
  77. redraw() {
  78. local i j
  79. local SPACE=" "
  80. local H="${HINT[0]}"
  81. $CLEAR
  82. echo "O-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..O"
  83. echo ") .- . | . , _ .'| | | | ("
  84. echo ") \`; | |\\| |\`. (_' |\`| :-: | | . ("
  85. echo ') .-. _`_ _ _ _ _________________`_`_`_`_ _ .-. ('
  86. echo ") \`.' \`--' \`--' '.' ("
  87. echo ") o~-~-~-~-~-~-~-~-~-~-~-~-~o o-~-~-~-~-~-~-~-~-~-~-~-~-o o-~-~-~-~-~-~-~-~-~o ("
  88. echo ") ; a b c d e f g h i j | ; a b c d e f g h i j | ; $H${SPACE:0:$((16-${#H}))} | ("
  89. local S1="|"
  90. local S2=";"
  91. for (( i = 0; i < 10; ++i )) ; do
  92. echo -n ") $S1 $(( (i + 1) % 10)) "
  93. for (( j = 0; j < 10; ++j )) ; do
  94. echo -n "${THY[$((i*10 + j))]} "
  95. done
  96. echo -n " $S2 $S1 $(( (i + 1) % 10)) "
  97. for (( j = 0; j < 10; ++j )) ; do
  98. echo -n "${FOE[$((i*10 + j))]} "
  99. done
  100. H="${HINT[$((i + 1))]}"
  101. local HINTX="$H${SPACE:0:$((16 - ${#H}))}"
  102. echo " $S2 $S1 $HINTX $S2 ("
  103. local SWAP="$S1"
  104. S1="$S2"
  105. S2="$SWAP"
  106. done
  107. echo ") o~-~-~-~-~-~-~-~-~-~-~-~-~o o-~-~-~-~-~-~-~-~-~-~-~-~-o o-~-~-~-~-~-~-~-~-~o ("
  108. echo ") ("
  109. local MSGX="$MSG${SPACE:0:$((72 - ${#MSG}))}"
  110. local MSG2X="$MSG2${SPACE:0:$((72 - ${#MSG2}))}"
  111. echo ") o-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~o ("
  112. echo ") | $MSGX | ("
  113. echo ") | $MSG2X | ("
  114. echo ") o~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-o ("
  115. echo -n "O-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..-~-..O"
  116. }
  117.  
  118. initfields() {
  119. local i j
  120. for (( i = 0; i < 100; ++i )) ; do
  121. THY[$i]=$CHAR_WATER
  122. FOE[$i]=$CHAR_WATER
  123. done
  124. }
  125.  
  126. placeship() {
  127. local imin=$((ROW - 1))
  128. local jmin=$((COL - 1))
  129. local imax=$((ROW + H + 1))
  130. local jmax=$((COL + W + 1))
  131. local i j
  132.  
  133. ((imin < 0)) && imin=0
  134. ((jmin < 0)) && jmin=0
  135. ((imax > 10)) && imax=10
  136. ((jmax > 10)) && jmax=10
  137.  
  138.  
  139. for (( i = imin; i < imax; ++i )) ; do
  140. for (( j = jmin; j < jmax; ++j )) ; do
  141. [[ "${THY[$((i*10 + j))]}" == "$CHAR_SHIP" ]] && return 1
  142. done
  143. done
  144.  
  145. for (( i = 0; i < H; ++i )) ; do
  146. for (( j = 0; j < W; ++j )) ; do
  147. THY[$(((ROW + i)*10 + j + COL))]="$CHAR_SHIP"
  148. THYSHIP[$(((ROW + i)*10 + j + COL))]=$(( 10 - LEFT ))
  149. done
  150. done
  151.  
  152. (( --LEFT ))
  153. (( --SHIPS[SHIP] ))
  154. (( SHIPS[SHIP] < 1 )) && (( ++SHIP ))
  155. (( SHIP > 3 )) && return 0
  156. (( W = 4 - SHIP ))
  157. (( H = 1 ))
  158. (( ROW = 0 ))
  159. (( COL = 0 ))
  160.  
  161. return 0
  162. }
  163.  
  164. arrayget() {
  165. local ARR="$1"
  166. local IDX="$2"
  167. eval echo \${$ARR[$IDX]}
  168. }
  169.  
  170. arrayset() {
  171. local ARR="$1"
  172. local IDX="$2"
  173. eval $ARR[$IDX]="$3"
  174. }
  175.  
  176. automiss() {
  177. local imin="$((CELL / 10))"
  178. local jmin="$((CELL % 10))"
  179. local imax="$imin"
  180. local jmax="$jmin"
  181. local c="$CELL"
  182. local i j
  183.  
  184. for (( c = $CELL; imin > 0; --imin )) ; do
  185. [ "$(arrayget $1 $c)" == "$CHAR_DEAD" ] || break
  186. (( c -= 10 ))
  187. done
  188. for (( c = $CELL; imax < 9; ++imax )) ; do
  189. [ "$(arrayget $1 $c)" == "$CHAR_DEAD" ] || break
  190. (( c += 10 ))
  191. done
  192. for (( c = $CELL; jmin > 0; --jmin )) ; do
  193. [ "$(arrayget $1 $c)" == "$CHAR_DEAD" ] || break
  194. (( c -= 1 ))
  195. done
  196. for (( c = $CELL; jmax < 9; ++jmax )) ; do
  197. [ "$(arrayget $1 $c)" == "$CHAR_DEAD" ] || break
  198. (( c += 1 ))
  199. done
  200.  
  201. for (( i = imin; i <= imax; ++i )) ; do
  202. for (( j = jmin; j <= jmax; ++j )) ; do
  203. echo "$i $j $(arrayget $1 $((i*10 + j)) )"
  204. if [ "$(arrayget $1 $((i*10 + j)) )" == "$CHAR_WATER" ] ; then
  205. arrayset $1 $((i*10 + j)) "$CHAR_MISS"
  206. fi
  207. done
  208. done
  209. }
  210.  
  211. checkconnection() {
  212. if [ -z "$COPROC_PID" ] ; then
  213. echo "Connection closed"
  214. quit
  215. fi
  216. }
  217.  
  218. placeships() {
  219. SHIPS=( 1 2 3 4 )
  220. THYDECKS=20
  221. THYSHIPDECKS=( 4 3 3 2 2 2 1 1 1 1 )
  222. FOEDECKS=20
  223. SHIP=0
  224. LEFT=10
  225. ROW=0
  226. COL=0
  227. W=4
  228. H=1
  229. WIN=0
  230. HINT=( "Keys:" " "
  231. "w Up "
  232. "a Left "
  233. "s Down "
  234. "d Right "
  235. "r Rotate ship "
  236. "p Place ship "
  237. )
  238. MSG="Place your ships."
  239.  
  240. while (( LEFT > 0 )) ; do
  241. BACKUP=( "${THY[@]}" )
  242. for (( i = 0; i < H; ++i )); do
  243. for (( j = 0; j < W; ++j )); do
  244. THY[$(( (i + ROW) * 10 + j + COL ))]="$CHAR_ACTIVE"
  245. done
  246. done
  247.  
  248. redraw
  249.  
  250. THY=( "${BACKUP[@]}" )
  251.  
  252. if ! read -sn 1 KEY ; then
  253. quit
  254. fi
  255. checkconnection
  256.  
  257. MSG="Place your ships."
  258. case "$KEY" in
  259. w) ((--ROW)) ;;
  260. a) ((--COL)) ;;
  261. s) ((++ROW)) ;;
  262. d) ((++COL)) ;;
  263. r) TMP=$W; W=$H; H=$TMP ;;
  264. p)
  265. if placeship ; then
  266. MSG="Ship placed."
  267. else
  268. MSG="Cannot place ship here because "
  269. MSG+="another ship is too close"
  270. fi
  271. ;;
  272. esac
  273.  
  274. if (( ROW < 0 )) ; then ROW=0 ; fi
  275. if (( COL < 0 )) ; then COL=0 ; fi
  276. if (( ROW + H > 10 )) ; then ((ROW = 10 - H)); fi
  277. if (( COL + W > 10 )) ; then ((COL = 10 - W)); fi
  278. done
  279. }
  280.  
  281. waitturn() {
  282. redraw
  283. while true; do
  284. checkconnection
  285. if ! read -u "${COPROC[0]}" CELL ; then
  286. echo "Connection closed"
  287. quit
  288. fi
  289.  
  290. if (( CELL < 0 || CELL > 99 )) ; then
  291. echo "Received malformed data, exiting"
  292. quit
  293. fi
  294.  
  295. CELL=$((CELL))
  296.  
  297. if [ "${THY[$CELL]}" == "$CHAR_SHIP" ] ; then
  298. THY["$CELL"]="$CHAR_DEAD"
  299. (( --THYDECKS ))
  300. (( --THYSHIPDECKS[THYSHIP[CELL]] ))
  301.  
  302. if (( THYSHIPDECKS[THYSHIP[CELL]] > 0 )) ; then
  303. MSG="Enemy has hit your ship!"
  304. echo hit >&"${COPROC[1]}"
  305. else
  306. MSG="Enemy has sunk your ship!"
  307. echo sunk >&"${COPROC[1]}"
  308. fi
  309.  
  310. if (( THYDECKS == 0 )) ; then
  311. MSG="All your ships have been sunk. You've lost the game :("
  312. return 1
  313. fi
  314. else
  315. if [ "${THY[$CELL]}" == "$CHAR_WATER" ] ; then
  316. THY["$CELL"]="$CHAR_MISS"
  317. fi
  318.  
  319. MSG="Enemy has missed. Your turn."
  320. echo miss >&"${COPROC[1]}"
  321. break
  322. fi
  323. redraw
  324. done
  325. redraw
  326. return 0
  327. }
  328.  
  329. hitfoe() {
  330. FOE["$CELL"]="$CHAR_DEAD"
  331. (( --FOEDECKS ))
  332. if (( FOEDECKS == 0 )) ; then
  333. MSG="Congratulations! You have won! :-)"
  334. WIN=1
  335. return 1
  336. fi
  337. return 0
  338. }
  339.  
  340. fire() {
  341. if [ "${FOE[$CELL]}" != "$CHAR_WATER" ] ; then
  342. MSG='Already shot there! Choose another target.'
  343. redraw
  344. return 1
  345. fi
  346.  
  347. checkconnection
  348. echo "$CELL" >&"${COPROC[1]}"
  349.  
  350. checkconnection
  351. if ! read -u "${COPROC[0]}" HIT ; then
  352. echo "Connection closed"
  353. quit
  354. fi
  355.  
  356. if [ "$HIT" == "hit" ] ; then
  357. MSG="You have hit! Your turn again."
  358. hitfoe || return 0
  359. return 1
  360. else
  361. if [ "$HIT" == "sunk" ] ; then
  362. MSG="You have sunk enemy's ship! Your turn again."
  363. hitfoe || return 0
  364. automiss FOE
  365. return 1
  366. fi
  367. fi
  368.  
  369. FOE["$CELL"]="$CHAR_MISS"
  370. MSG="You have missed. Now waiting enemy's turn..."
  371. redraw
  372.  
  373. return 0
  374. }
  375.  
  376. maketurn() {
  377. COL=0
  378. ROW=0
  379. HINT=( "Keys: "
  380. " "
  381. "w Up" "a Left" "s Down" "d Right" "f Fire"
  382. )
  383.  
  384. while true; do
  385. CELL=$(( ROW*10 + COL ))
  386. BACKUP="${FOE[$CELL]}"
  387. FOE[$CELL]="$CHAR_CROSSHAIR"
  388. redraw
  389. FOE[$CELL]=$BACKUP
  390.  
  391. if ! read -sn 1 KEY ; then
  392. quit
  393. fi
  394. checkconnection
  395.  
  396. case "$KEY" in
  397. w) ((--ROW)) ;;
  398. a) ((--COL)) ;;
  399. s) ((++ROW)) ;;
  400. d) ((++COL)) ;;
  401. f) fire && break ;;
  402. esac
  403.  
  404. (( ROW < 0 )) && ROW=0
  405. (( COL < 0 )) && COL=0
  406. (( ROW >= 10 )) && ROW=9
  407. (( COL >= 10 )) && COL=9
  408. done
  409.  
  410. return $WIN
  411. }
  412.  
  413. $STTY -echo
  414.  
  415. # Parse command-line options
  416. SERVE=0
  417. HOST=""
  418. PORT=21366
  419.  
  420. while (( $# > 0 )) ; do
  421. case "$1" in
  422. "-serve")
  423. SERVE=1
  424. ;;
  425. "-p")
  426. if (( $# < 2 )); then usage; fi
  427. PORT=$2
  428. shift
  429. ;;
  430. -*)
  431. echo "Unknown option $1"
  432. usage
  433. ;;
  434. *)
  435. if [ -n "$HOST" ]; then usage; fi
  436. HOST=$1
  437. ;;
  438. esac
  439. shift
  440. done
  441.  
  442. if [ -z "$HOST" ] && (( SERVE == 0 )) ; then
  443. echo "Need a hostname."
  444. usage
  445. fi
  446.  
  447. $CLEAR
  448. HELLOSTR="Hi, I want to play a game with you"
  449.  
  450. if (( SERVE == 1 )) ; then
  451. MSG1="Waiting for incoming"
  452. MSG2="connection on port $PORT..."
  453. title
  454. coproc $NETCAT -q 0 -l -p "$PORT"
  455. READTIMEOUT=180
  456. else
  457. MSG1="Connecting to"
  458. MSG2="$HOST:$PORT..."
  459. coproc $NETCAT -w 5 -q 0 "$HOST" "$PORT"
  460. READTIMEOUT=5
  461. fi
  462.  
  463. echo "$HELLOSTR" >&${COPROC[1]}
  464.  
  465. checkconnection
  466. if ! read -t "$READTIMEOUT" -u "${COPROC[0]}" HELLO ; then
  467. echo "Failed to connect, exiting."
  468. quit 1
  469. fi
  470.  
  471. if [ "$HELLO" != "$HELLOSTR" ] ; then
  472. echo "Unknown peer at the other end of connection, exiting."
  473. quit 1
  474. fi
  475.  
  476. #echo "Connected! Press ENTER to start placing ships!"
  477. MSG1="Connected!"
  478. MSG2=""
  479. MSG3="Press ENTER to start"
  480. MSG4="placing ships"
  481. title
  482. read
  483.  
  484. checkconnection
  485.  
  486. while true; do
  487. MSG=""
  488. MSG2=""
  489. initfields
  490. placeships
  491. HINT=()
  492.  
  493. MSG="All ships placed. Waiting for the enemy..."
  494. redraw
  495. checkconnection
  496.  
  497. THYNUMBER=$RANDOM
  498. echo "$THYNUMBER" >&${COPROC[1]}
  499.  
  500. checkconnection
  501. if ! read -u "${COPROC[0]}" FOENUMBER ; then
  502. echo "Connection closed"
  503. quit
  504. fi
  505.  
  506. if (( FOENUMBER > THYNUMBER )) ; then
  507. MSG="Enemy's turn is first. Waiting..."
  508. waitturn || LOST=1
  509. else
  510. MSG="You turn."
  511. fi
  512.  
  513. while ((LOST != 1)); do
  514. maketurn || break
  515. HINT=()
  516. waitturn || break
  517. done
  518. HINT=()
  519.  
  520. MSG2="Play again? (y/n)"
  521. redraw
  522.  
  523. while read -n 1 YESNO ; do
  524. checkconnection
  525. if [ "$YESNO" == 'y' ] || [ "$YESNO" == 'Y' ] ; then
  526. continue 2
  527. fi
  528. if [ "$YESNO" == 'n' ] || [ "$YESNO" == 'N' ] ; then
  529. break 2
  530. fi
  531. done
  532. break
  533. done
  534.  
  535. if [ -n "$COPROC_PID" ] ; then
  536. kill $COPROC_PID
  537. wait $COPROC_PID
  538. fi
  539.  
  540. quit

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.