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

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.