2021-06-29 23:48:55 +02:00
# File: Battleship.pm
#
# Purpose: Simplified version of the Battleship board game.
#
# Note: This code was written circa 1993 for a DikuMUD fork. It was originally
# written in C, as I was teaching the language to myself in my early teens. Two
# decades or so later, I transliterated this code from C to Perl for PBot. Much
# of the "ugly" C-style design of this code has been preserved for personal
# historical reasons -- I was inspired by the IOCCC and I attempted to be clever
# with nested conditional operators and other silliness. Please be gentle if you
# read this code. :)
2018-07-01 12:07:44 +02:00
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
2019-09-01 20:01:18 +02:00
package Plugins::Battleship ;
2020-02-09 04:48:05 +01:00
use parent 'Plugins::Plugin' ;
2018-07-01 12:07:44 +02:00
2021-06-19 06:23:34 +02:00
use PBot::Imports ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
use Time::Duration ;
2018-07-02 04:43:27 +02:00
use Data::Dumper ;
2018-07-01 12:07:44 +02:00
sub initialize {
2020-02-15 23:38:32 +01:00
my ( $ self , % conf ) = @ _ ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
# register `battleship` bot command
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_battleship ( @ _ ) } , 'battleship' , 0 ) ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
# set the channel where to send game messages
2020-02-15 23:38:32 +01:00
$ self - > { channel } = $ self - > { pbot } - > { registry } - > get_value ( 'battleship' , 'channel' ) // '##battleship' ;
2021-06-29 23:48:55 +02:00
# set debugging flag
2020-02-15 23:38:32 +01:00
$ self - > { debug } = $ self - > { pbot } - > { registry } - > get_value ( 'battleship' , 'debug' ) // 0 ;
2020-01-22 06:03:47 +01:00
2021-06-29 23:48:55 +02:00
# set board tile symbols/characters
2020-02-15 23:38:32 +01:00
$ self - > { player_one_vert } = '|' ;
$ self - > { player_one_horiz } = '—' ;
$ self - > { player_two_vert } = 'I' ;
$ self - > { player_two_horiz } = '=' ;
2021-06-29 23:48:55 +02:00
$ self - > { ocean } = '~' ;
$ self - > { player_one_miss } = '*' ;
$ self - > { player_one_hit } = '1' ;
$ self - > { player_two_miss } = 'o' ;
$ self - > { player_two_hit } = '2' ;
2020-01-22 06:03:47 +01:00
2021-06-29 23:48:55 +02:00
# create game state machine
2020-02-15 23:38:32 +01:00
$ self - > create_states ;
2018-07-01 12:07:44 +02:00
}
sub unload {
2021-06-29 23:48:55 +02:00
my ( $ self ) = @ _ ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
# unregister `battleship` bot command
$ self - > { pbot } - > { commands } - > unregister ( 'battleship' ) ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
# remove battleship loop event from event queue
$ self - > { pbot } - > { event_queue } - > dequeue_event ( 'battleship loop' ) ;
2018-07-01 12:07:44 +02:00
}
2021-06-29 23:48:55 +02:00
# `battleship` bot command
2020-05-04 22:21:35 +02:00
sub cmd_battleship {
my ( $ self , $ context ) = @ _ ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
my $ usage = "Usage: battleship challenge|accept|decline|bomb|board|score|forfeit|quit|players|kick|abort; for more information about a command: battleship help <command>" ;
# strip leading and trailing whitespace
$ context - > { arguments } =~ s/^\s+|\s+$//g ;
2018-07-01 12:07:44 +02:00
2020-05-04 22:21:35 +02:00
my ( $ command , $ arguments ) = split / / , $ context - > { arguments } , 2 ;
2021-06-29 23:48:55 +02:00
$ command // = '' ;
2020-02-15 23:38:32 +01:00
$ command = lc $ command ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
# shorter aliases
my ( $ nick , $ user , $ host , $ hostmask , $ channel ) = (
$ context - > { nick } ,
$ context - > { user } ,
$ context - > { host } ,
$ context - > { hostmask } ,
$ self - > { channel } ,
) ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
given ( $ command ) {
2021-06-29 23:48:55 +02:00
# help doesn't do much yet
2018-07-01 12:07:44 +02:00
when ( 'help' ) {
2020-02-15 23:38:32 +01:00
given ( $ arguments ) {
2021-06-29 23:48:55 +02:00
when ( 'help' ) {
return "Seriously?" ;
}
2020-02-15 23:38:32 +01:00
default {
2021-06-29 23:48:55 +02:00
if ( length $ arguments ) {
return "Battleship help is coming soon." ;
} else {
return "Usage: battleship help <command>" ;
}
2020-02-15 23:38:32 +01:00
}
}
2018-07-01 12:07:44 +02:00
}
2021-06-29 23:48:55 +02:00
# issue a challenge to begin a game
2020-02-15 23:38:32 +01:00
when ( 'challenge' ) {
2021-06-29 23:48:55 +02:00
if ( $ self - > { current_state } ne 'nogame' ) {
return "There is already a game of Battleship underway." ;
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
# `challenge` without arguments issues an open challenge
2020-02-15 23:38:32 +01:00
if ( not length $ arguments ) {
2021-06-29 23:48:55 +02:00
$ self - > set_state ( 'accept' ) ;
# add player 1, the challenger, to the game
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account ( $ nick , $ user , $ host ) ;
my $ player = {
id = > $ id ,
name = > $ nick ,
missedinputs = > 0
} ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
push @ { $ self - > { state_data } - > { players } } , $ player ;
2018-07-02 03:46:58 +02:00
2021-06-29 23:48:55 +02:00
# add player 2, a placeholder for the challengee
$ player = {
id = > - 1 ,
name = > 'anybody' ,
missedinputs = > 0
} ;
2020-02-15 23:38:32 +01:00
push @ { $ self - > { state_data } - > { players } } , $ player ;
2020-03-08 04:27:15 +01:00
2021-06-29 23:48:55 +02:00
# start the battleship game loop
2021-06-22 02:26:24 +02:00
$ self - > { pbot } - > { event_queue } - > enqueue_event ( sub {
2020-03-08 04:27:15 +01:00
$ self - > run_one_state ;
} , 1 , 'battleship loop' , 1
) ;
2021-06-29 23:48:55 +02:00
return "/msg $channel $nick has made an open challenge! Use `accept` to accept their challenge." ;
}
# otherwise we're challenging a specific person
# are they in the channel?
my $ challengee = $ self - > { pbot } - > { nicklist } - > is_present ( $ channel , $ arguments ) ;
if ( not $ challengee ) {
return "$arguments is not present in $channel. Invite them to the channel and try again!" ;
2020-02-15 23:38:32 +01:00
}
2018-07-02 03:46:58 +02:00
2021-06-29 23:48:55 +02:00
# set up next state of game
$ self - > set_state ( 'accept' ) ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
# add player 1, the challenger, to the game
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account ( $ nick , $ user , $ host ) ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
my $ player = {
id = > $ id ,
name = > $ nick ,
missedinputs = > 0 ,
} ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
push @ { $ self - > { state_data } - > { players } } , $ player ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
# add player 2, the challengee, to the game
2020-02-15 23:38:32 +01:00
( $ id ) = $ self - > { pbot } - > { messagehistory } - > { database } - > find_message_account_by_nick ( $ challengee ) ;
2021-06-29 23:48:55 +02:00
$ player = {
id = > $ id ,
name = > $ challengee ,
missedinputs = > 0 ,
} ;
2020-02-15 23:38:32 +01:00
push @ { $ self - > { state_data } - > { players } } , $ player ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
# start the battleship game loop
2021-06-22 02:26:24 +02:00
$ self - > { pbot } - > { event_queue } - > enqueue_event ( sub {
2021-06-23 20:45:53 +02:00
$ self - > run_one_state ;
2020-03-08 04:27:15 +01:00
} , 1 , 'battleship loop' , 1
) ;
2021-06-29 23:48:55 +02:00
return "/msg $channel $nick has challenged $challengee to Battleship! Use `accept` to accept their challenge." ;
2020-02-15 23:38:32 +01:00
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
# accept challenges
2020-02-15 23:38:32 +01:00
when ( 'accept' ) {
2021-06-29 23:48:55 +02:00
if ( $ self - > { current_state } ne 'accept' ) {
return "/msg $nick This is not the time to use `accept`." ;
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account ( $ nick , $ user , $ host ) ;
2020-02-15 23:38:32 +01:00
my $ player = $ self - > { state_data } - > { players } - > [ 1 ] ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
# accept an open challenge
2020-02-15 23:38:32 +01:00
if ( $ player - > { id } == - 1 ) {
$ player - > { id } = $ id ;
2021-06-29 23:48:55 +02:00
$ player - > { name } = $ nick ;
2020-02-15 23:38:32 +01:00
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
# confirm right user is accepting challenge
2020-02-15 23:38:32 +01:00
if ( $ player - > { id } == $ id ) {
2021-06-29 23:48:55 +02:00
# accept the challenge
2020-02-15 23:38:32 +01:00
$ player - > { accepted } = 1 ;
2021-06-29 23:48:55 +02:00
return "/msg $channel $nick has accepted $self->{state_data}->{players}->[0]->{name}'s challenge!" ;
2020-02-15 23:38:32 +01:00
} else {
2021-06-29 23:48:55 +02:00
# wrong user tried to accept
return "/msg $nick You have not been challenged to a game of Battleship." ;
2020-02-15 23:38:32 +01:00
}
2018-07-01 12:07:44 +02:00
}
2021-06-29 23:48:55 +02:00
# decline a challenge or forfeit/concede a game
when ( [ 'decline' , 'quit' , 'forfeit' , 'concede' ] ) {
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account ( $ nick , $ user , $ host ) ;
2020-02-15 23:38:32 +01:00
my $ removed = 0 ;
for ( my $ i = 0 ; $ i < @ { $ self - > { state_data } - > { players } } ; $ i + + ) {
if ( $ self - > { state_data } - > { players } - > [ $ i ] - > { id } == $ id ) {
$ self - > { state_data } - > { players } - > [ $ i ] - > { removed } = 1 ;
$ removed = 1 ;
}
}
if ( $ removed ) {
2021-06-29 23:48:55 +02:00
if ( $ self - > { current_state } eq 'accept' ) {
$ self - > set_state ( 'nogame' ) ;
$ self - > { state_data } - > { players } = [] ;
return "/msg $channel $nick declined the challenge." ;
}
else {
return "/msg $channel $nick has left the game!" ;
2020-02-15 23:38:32 +01:00
}
2021-06-29 23:48:55 +02:00
}
else {
return "$nick: But you are not even playing the game." ;
2020-02-15 23:38:32 +01:00
}
2018-07-01 12:07:44 +02:00
}
2018-07-05 23:46:53 +02:00
2020-02-15 23:38:32 +01:00
when ( 'abort' ) {
2021-06-29 23:48:55 +02:00
if ( not $ self - > { pbot } - > { users } - > loggedin_admin ( $ channel , $ hostmask ) ) {
return "$nick: Only admins may abort the game." ;
2020-05-04 22:21:35 +02:00
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
$ self - > set_state ( 'gameover' ) ;
return "/msg $channel $nick: The game has been aborted." ;
2020-02-15 23:38:32 +01:00
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
when ( 'score' ) {
if ( @ { $ self - > { state_data } - > { players } } == 2 ) {
$ self - > show_scoreboard ;
2021-06-29 23:48:55 +02:00
return '' ;
2020-02-15 23:38:32 +01:00
} else {
return "There is no game going on right now." ;
}
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
when ( 'players' ) {
2021-06-29 23:48:55 +02:00
if ( $ self - > { current_state } eq 'accept' ) {
return "$self->{state_data}->{players}->[0]->{name} has challenged $self->{state_data}->{players}->[1]->{name}!" ;
}
elsif ( @ { $ self - > { state_data } - > { players } } == 2 ) {
return "$self->{state_data}->{players}->[0]->{name} is in battle with $self->{state_data}->{players}->[1]->{name}!" ;
}
else {
return "There are no players playing right now. Start a game with `challenge <nick>`!" ;
}
2020-02-15 23:38:32 +01:00
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
when ( 'kick' ) {
2021-06-29 23:48:55 +02:00
if ( not $ self - > { pbot } - > { users } - > loggedin_admin ( $ channel , $ hostmask ) ) {
return "$nick: Only admins may kick people from the game." ;
2020-05-04 22:21:35 +02:00
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
if ( not length $ arguments ) {
return "Usage: battleship kick <nick>" ;
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
my $ removed = 0 ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
for ( my $ i = 0 ; $ i < @ { $ self - > { state_data } - > { players } } ; $ i + + ) {
if ( lc $ self - > { state_data } - > { players } - > [ $ i ] - > { name } eq $ arguments ) {
$ self - > { state_data } - > { players } - > [ $ i ] - > { removed } = 1 ;
$ removed = 1 ;
}
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
if ( $ removed ) {
2021-06-29 23:48:55 +02:00
return "/msg $channel $nick: $arguments has been kicked from the game." ;
2020-02-15 23:38:32 +01:00
} else {
2021-06-29 23:48:55 +02:00
return "$nick: $arguments isn't even in the game." ;
2020-02-15 23:38:32 +01:00
}
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
when ( 'bomb' ) {
2021-06-29 23:48:55 +02:00
if ( $ self - > { current_state } ne 'playermove' and $ self - > { current_state } ne 'checkplayer' ) {
return "$nick: It's not time to do that now." ;
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account ( $ nick , $ user , $ host ) ;
2018-07-03 08:33:22 +02:00
2020-02-15 23:38:32 +01:00
my $ player ;
2018-07-03 08:33:22 +02:00
2021-06-29 23:48:55 +02:00
if ( $ self - > { state_data } - > { players } - > [ 0 ] - > { id } == $ id ) {
$ player = 0 ;
}
elsif ( $ self - > { state_data } - > { players } - > [ 1 ] - > { id } == $ id ) {
$ player = 1 ;
}
else {
return "You are not playing in this game." ;
}
2018-07-04 05:03:49 +02:00
2021-06-29 23:48:55 +02:00
# no arguments provided
2020-02-15 23:38:32 +01:00
if ( not length $ arguments ) {
2021-06-29 23:48:55 +02:00
if ( delete $ self - > { state_data } - > { players } - > [ $ player ] - > { location } ) {
return "$nick: Attack location cleared." ;
} else {
return "$nick: Usage: bomb <location>" ;
}
2020-02-15 23:38:32 +01:00
}
2018-07-03 08:33:22 +02:00
2021-06-29 23:48:55 +02:00
# validate arguments
2020-02-15 23:38:32 +01:00
$ arguments = uc $ arguments ;
2018-07-03 08:33:22 +02:00
2021-06-29 23:48:55 +02:00
if ( $ arguments !~ m/^[A-Z][0-9]+$/ ) {
return "$nick: Usage: bomb <location>; <location> must be in the form of A15, B3, C9, etc." ;
}
# ensure arguments are within range of battlefield
my ( $ x , $ y ) = $ arguments =~ m/^(.)(.*)/ ;
2018-07-03 08:33:22 +02:00
2020-02-15 23:38:32 +01:00
$ x = ord ( $ x ) - 65 ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
if ( $ x < 0 || $ x > $ self - > { N_Y } || $ y < 0 || $ y > $ self - > { N_X } ) {
return "$nick: Target out of range, try again." ;
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
# it's not this player's turn, go ahead and store their move
# for when it is their turn
2020-02-15 23:38:32 +01:00
if ( $ self - > { state_data } - > { current_player } != $ player ) {
my $ msg ;
2021-06-29 23:48:55 +02:00
if ( not exists $ self - > { state_data } - > { players } - > [ $ player ] - > { location } ) {
$ msg = "$nick: You will attack $arguments when it is your turn." ;
}
else {
$ msg = "$nick: You will now attack $arguments instead of $self->{state_data}->{players}->[$player]->{location} when it is your turn." ;
}
2020-02-15 23:38:32 +01:00
$ self - > { state_data } - > { players } - > [ $ player ] - > { location } = $ arguments ;
return $ msg ;
}
2018-07-02 02:01:18 +02:00
2021-06-29 23:48:55 +02:00
# prevent player from attacking multiple times in one turn
if ( $ self - > { player } - > [ $ player ] - > { done } ) {
return "$nick: You have already attacked this turn." ;
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
# commence attack!
if ( $ self - > bomb ( $ player , $ arguments ) ) {
2020-02-15 23:38:32 +01:00
if ( $ self - > { player } - > [ $ player ] - > { won } ) {
2021-06-29 23:48:55 +02:00
$ self - > set_state ( 'checkplayer' ) ;
2020-02-15 23:38:32 +01:00
$ self - > run_one_state ;
} else {
2021-06-29 23:48:55 +02:00
$ self - > { player } - > [ $ player ] - > { done } = 1 ;
$ self - > { player } - > [ ! $ player ] - > { done } = 0 ;
2020-02-15 23:38:32 +01:00
$ self - > { state_data } - > { current_player } = ! $ player ;
2021-06-29 23:48:55 +02:00
$ self - > { state_data } - > { ticks } = 1 ;
$ self - > { state_data } - > { first_tock } = 1 ;
$ self - > { state_data } - > { tocks } = 0 ;
2020-02-15 23:38:32 +01:00
}
}
2021-06-29 23:48:55 +02:00
# bomb() sent bombing output to channel
return '' ;
2020-02-15 23:38:32 +01:00
}
2019-06-26 18:34:19 +02:00
2021-06-29 23:48:55 +02:00
when ( [ 'specboard' , 'board' ] ) {
if ( grep { $ _ eq $ self - > { current_state } } qw/nogame accept genboard gameover/ ) {
return "$nick: There is no board to show right now." ;
2020-02-15 23:38:32 +01:00
}
2021-06-29 23:48:55 +02:00
# specifically show spectator board, even if invoked by a player
2020-02-15 23:38:32 +01:00
if ( $ _ eq 'specboard' ) {
$ self - > show_battlefield ( 2 ) ;
2021-06-29 23:48:55 +02:00
return '' ;
2020-02-15 23:38:32 +01:00
}
2021-06-29 23:48:55 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account ( $ nick , $ user , $ host ) ;
# show player's personal board if `id` is playing
2020-02-15 23:38:32 +01:00
for ( my $ i = 0 ; $ i < 2 ; $ i + + ) {
if ( $ self - > { state_data } - > { players } - > [ $ i ] - > { id } == $ id ) {
2021-06-29 23:48:55 +02:00
$ self - > send_message ( $ channel , "$nick surveys the battlefield!" ) ;
2020-02-15 23:38:32 +01:00
$ self - > show_battlefield ( $ i ) ;
2021-06-29 23:48:55 +02:00
return '' ;
2020-02-15 23:38:32 +01:00
}
}
2021-06-29 23:48:55 +02:00
# otherwise show spectator board
2020-02-15 23:38:32 +01:00
$ self - > show_battlefield ( 2 ) ;
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
# this command shows both player's ships and all information
2020-02-15 23:38:32 +01:00
when ( 'fullboard' ) {
2021-06-29 23:48:55 +02:00
if ( not $ self - > { pbot } - > { users } - > loggedin_admin ( $ channel , $ hostmask ) ) {
return "$nick: Only admins may see the full board." ;
2020-05-04 22:21:35 +02:00
}
2020-02-15 23:38:32 +01:00
2021-06-29 23:48:55 +02:00
if ( grep { $ _ eq $ self - > { current_state } } qw/nogame accept genboard gameover/ ) {
return "$nick: There is no board to show right now." ;
2020-02-15 23:38:32 +01:00
}
# show real board if admin is actually in the game ... no cheating!
2021-06-29 23:48:55 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account ( $ nick , $ user , $ host ) ;
2020-02-15 23:38:32 +01:00
for ( my $ i = 0 ; $ i < 2 ; $ i + + ) {
if ( $ self - > { state_data } - > { players } - > [ $ i ] - > { id } == $ id ) {
2021-06-29 23:48:55 +02:00
$ self - > send_message ( $ channel , "$nick surveys the battlefield!" ) ;
2020-02-15 23:38:32 +01:00
$ self - > show_battlefield ( $ i ) ;
2021-06-29 23:48:55 +02:00
return '' ;
2020-02-15 23:38:32 +01:00
}
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
# show full board
$ self - > show_battlefield ( 4 , $ nick ) ;
2020-02-15 23:38:32 +01:00
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
default { return $ usage ; }
2018-07-01 12:07:44 +02:00
}
}
2021-06-29 23:48:55 +02:00
# add a message to PBot output queue, optionally with a delay
2018-07-01 12:07:44 +02:00
sub send_message {
2020-02-15 23:38:32 +01:00
my ( $ self , $ to , $ text , $ delay ) = @ _ ;
2021-06-23 20:45:53 +02:00
2021-06-29 23:48:55 +02:00
$ delay // = 0 ;
2021-06-23 20:45:53 +02:00
2020-02-15 23:38:32 +01:00
my $ botnick = $ self - > { pbot } - > { registry } - > get_value ( 'irc' , 'botnick' ) ;
2021-06-24 01:09:39 +02:00
2020-02-15 23:38:32 +01:00
my $ message = {
2021-06-21 00:10:16 +02:00
nick = > $ botnick ,
user = > 'battleship' ,
host = > 'localhost' ,
hostmask = > "$botnick!battleship\@localhost" ,
command = > 'battleship' ,
checkflood = > 1 ,
message = > $ text
2020-02-15 23:38:32 +01:00
} ;
2021-06-24 01:09:39 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { interpreter } - > add_message_to_output_queue ( $ to , $ message , $ delay ) ;
2018-07-01 12:07:44 +02:00
}
2021-06-29 23:48:55 +02:00
# some colors for IRC messages
my % color = (
white = > "\x0300" ,
black = > "\x0301" ,
blue = > "\x0302" ,
green = > "\x0303" ,
red = > "\x0304" ,
maroon = > "\x0305" ,
purple = > "\x0306" ,
orange = > "\x0307" ,
yellow = > "\x0308" ,
lightgreen = > "\x0309" ,
teal = > "\x0310" ,
cyan = > "\x0311" ,
lightblue = > "\x0312" ,
magneta = > "\x0313" ,
gray = > "\x0314" ,
lightgray = > "\x0315" ,
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
bold = > "\x02" ,
italics = > "\x1D" ,
underline = > "\x1F" ,
reverse = > "\x16" ,
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
reset = > "\x0F" ,
) ;
2018-07-01 12:07:44 +02:00
# battleship stuff
sub init_game {
2020-02-15 23:38:32 +01:00
my ( $ self , $ nick1 , $ nick2 ) = @ _ ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { N_X } = 15 ;
$ self - > { N_Y } = 8 ;
$ self - > { SHIPS } = 6 ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
for ( my $ ship = 0 ; $ ship < $ self - > { SHIPS } ; $ ship + + ) {
$ self - > { ship_length } - > [ $ ship ] = 0 ;
}
2018-07-03 08:33:22 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { board } = [] ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { player } = [
2021-06-29 23:48:55 +02:00
{ bombs = > 0 , hit = > 0 , miss = > 0 , sunk = > 0 , nick = > $ nick1 , done = > 0 } ,
{ bombs = > 0 , hit = > 0 , miss = > 0 , sunk = > 0 , nick = > $ nick2 , done = > 0 } ,
2020-02-15 23:38:32 +01:00
] ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { horiz } = 0 ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > generate_battlefield ;
2018-07-01 12:07:44 +02:00
}
sub count_ship_sections {
2020-02-15 23:38:32 +01:00
my ( $ self , $ player ) = @ _ ;
2021-06-29 23:48:55 +02:00
my $ sections = 0 ;
2020-02-15 23:38:32 +01:00
2021-06-29 23:48:55 +02:00
for ( my $ x = 0 ; $ x < $ self - > { N_Y } ; $ x + + ) {
for ( my $ y = 0 ; $ y < $ self - > { N_X } ; $ y + + ) {
2020-02-15 23:38:32 +01:00
if ( $ player == 0 ) {
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ x ] [ $ y ] eq $ self - > { player_two_vert }
|| $ self - > { board } - > [ $ x ] [ $ y ] eq $ self - > { player_two_horiz } )
{
$ sections + + ;
}
2020-02-15 23:38:32 +01:00
} else {
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ x ] [ $ y ] eq $ self - > { player_one_vert }
|| $ self - > { board } - > [ $ x ] [ $ y ] eq $ self - > { player_one_horiz } )
{
$ sections + + ;
}
2020-02-15 23:38:32 +01:00
}
2018-07-01 12:07:44 +02:00
}
}
2020-02-15 23:38:32 +01:00
return $ sections ;
2018-07-01 12:07:44 +02:00
}
sub check_ship {
2020-02-15 23:38:32 +01:00
my ( $ self , $ x , $ y , $ o , $ d , $ l ) = @ _ ;
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
my ( $ xd , $ yd , $ i ) ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
if ( ! $ o ) {
if ( ! $ d ) {
$ yd = - 1 ;
2021-06-29 23:48:55 +02:00
if ( $ y - $ l < 0 ) {
return 0 ;
}
2020-02-15 23:38:32 +01:00
} else {
$ yd = 1 ;
2021-06-29 23:48:55 +02:00
if ( $ y + $ l >= $ self - > { N_X } ) {
return 0 ;
}
2020-02-15 23:38:32 +01:00
}
$ xd = 0 ;
2018-07-01 12:07:44 +02:00
} else {
2020-02-15 23:38:32 +01:00
if ( ! $ d ) {
$ xd = - 1 ;
2021-06-29 23:48:55 +02:00
if ( $ x - $ l < 0 ) {
return 0 ;
}
2020-02-15 23:38:32 +01:00
} else {
$ xd = 1 ;
2021-06-29 23:48:55 +02:00
if ( $ x + $ l >= $ self - > { N_Y } ) {
return 0 ;
}
2020-02-15 23:38:32 +01:00
}
$ yd = 0 ;
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
for ( my $ i = 0 ; $ i < $ l ; $ i + + ) {
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ x += $ o ? $ xd : 0 ] [ $ y += $ o ? 0 : $ yd ] ne $ self - > { ocean } ) {
return 0 ;
}
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
return 1 ;
2018-07-01 12:07:44 +02:00
}
sub number {
2020-02-15 23:38:32 +01:00
my ( $ self , $ lower , $ upper ) = @ _ ;
2021-06-29 23:48:55 +02:00
return int rand ( $ upper - $ lower ) + $ lower ;
2018-07-01 12:07:44 +02:00
}
2018-07-03 08:33:22 +02:00
sub generate_ship {
2020-02-15 23:38:32 +01:00
my ( $ self , $ player , $ ship ) = @ _ ;
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
my ( $ x , $ y , $ o , $ d , $ i , $ l ) ;
my ( $ yd , $ xd ) = ( 0 , 0 ) ;
my $ fail = 0 ;
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
while ( 1 ) {
$ x = $ self - > number ( 0 , $ self - > { N_Y } ) ;
$ y = $ self - > number ( 0 , $ self - > { N_X } ) ;
$ o = $ self - > number ( 1 , 10 ) < 6 ;
$ d = $ self - > number ( 1 , 10 ) < 6 ;
2021-06-29 23:48:55 +02:00
if ( $ self - > { ship_length } - > [ $ ship ] ) {
$ l = $ self - > { ship_length } - > [ $ ship ] ;
} else {
$ l = $ self - > number ( 3 , 6 ) ;
}
2020-02-15 23:38:32 +01:00
2021-06-29 23:48:55 +02:00
if ( $ self - > { debug } ) {
$ self - > { pbot } - > { logger } - > log ( "generate ships player $player: ship $ship x,y: $x,$y o,d: $o,$d length: $l\n" ) ;
}
2020-02-15 23:38:32 +01:00
if ( $ self - > check_ship ( $ x , $ y , $ o , $ d , $ l ) ) {
if ( ! $ o ) {
2021-06-29 23:48:55 +02:00
if ( $ self - > { horiz } < 2 ) {
next ;
}
if ( ! $ d ) {
$ yd = - 1 ;
} else {
$ yd = 1 ;
}
2020-02-15 23:38:32 +01:00
$ xd = 0 ;
} else {
$ self - > { horiz } + + ;
2021-06-29 23:48:55 +02:00
if ( ! $ d ) {
$ xd = - 1 ;
} else {
$ xd = 1 ;
}
2020-02-15 23:38:32 +01:00
$ yd = 0 ;
}
for ( my $ i = 0 ; $ i < $ l ; $ i + + ) {
$ self - > { board } - > [ $ x += $ o ? $ xd : 0 ] [ $ y += $ o ? 0 : $ yd ] =
$ player ? ( $ o ? $ self - > { player_two_vert } : $ self - > { player_two_horiz } ) : ( $ o ? $ self - > { player_one_vert } : $ self - > { player_one_horiz } ) ;
}
$ self - > { ship_length } - > [ $ ship ] = $ l ;
return 1 ;
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
if ( + + $ fail >= 5000 ) {
$ self - > { pbot } - > { logger } - > log ( "Failed to generate ship\n" ) ;
$ self - > send_message ( $ self - > { channel } , "Failed to place a ship. I cannot continue. Game over." ) ;
2021-06-29 23:48:55 +02:00
$ self - > set_state ( 'nogame' ) ;
2020-02-15 23:38:32 +01:00
return 0 ;
}
2018-07-01 12:07:44 +02:00
}
}
sub generate_battlefield {
2020-02-15 23:38:32 +01:00
my ( $ self ) = @ _ ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
for ( my $ y = 0 ; $ y < $ self - > { N_Y } ; $ y + + ) {
for ( my $ x = 0 ; $ x < $ self - > { N_X } ; $ x + + ) {
$ self - > { board } - > [ $ y ] [ $ x ] = $ self - > { ocean } ;
}
2018-07-01 12:07:44 +02:00
}
2021-06-29 23:48:55 +02:00
for ( my $ x = 0 ; $ x < $ self - > { SHIPS } ; $ x + + ) {
if ( ! $ self - > generate_ship ( 0 , $ x ) || ! $ self - > generate_ship ( 1 , $ x ) ) {
return 0 ;
}
2018-07-03 08:33:22 +02:00
}
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
return 1 ;
2018-07-01 12:07:44 +02:00
}
sub check_sunk {
2020-02-15 23:38:32 +01:00
my ( $ self , $ x , $ y , $ player ) = @ _ ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
my $ target = $ self - > { board } - > [ $ x ] [ $ y ] ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
given ( $ target ) {
when ( $ _ eq $ self - > { player_two_vert } or $ _ eq $ self - > { player_one_vert } ) {
2021-06-29 23:48:55 +02:00
for ( my $ i = $ x + 1 ; $ i < $ self - > { N_Y } ; $ i + + ) {
if ( ( $ self - > { board } - > [ $ i ] [ $ y ] eq $ self - > { player_one_vert } && $ player )
|| ( $ self - > { board } - > [ $ i ] [ $ y ] eq $ self - > { player_two_vert } && ! $ player ) )
{
return 0 ;
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ i ] [ $ y ] eq $ self - > { ocean }
|| $ self - > { board } - > [ $ i ] [ $ y ] eq $ self - > { player_one_miss }
|| $ self - > { board } - > [ $ i ] [ $ y ] eq $ self - > { player_two_miss } )
{
last ;
}
2020-02-15 23:38:32 +01:00
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
for ( my $ i = $ x - 1 ; $ i >= 0 ; $ i - - ) {
if ( ( $ self - > { board } - > [ $ i ] [ $ y ] eq $ self - > { player_one_vert } && $ player )
|| ( $ self - > { board } - > [ $ i ] [ $ y ] eq $ self - > { player_two_vert } && ! $ player ) )
{
return 0 ;
}
2020-02-15 23:38:32 +01:00
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ i ] [ $ y ] eq $ self - > { ocean }
|| $ self - > { board } - > [ $ i ] [ $ y ] eq $ self - > { player_one_miss }
|| $ self - > { board } - > [ $ i ] [ $ y ] eq $ self - > { player_two_miss } )
{
last ;
}
2020-02-15 23:38:32 +01:00
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
return 1 ;
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
when ( $ _ eq $ self - > { player_one_horiz } or $ _ eq $ self - > { player_two_horiz } ) {
2021-06-29 23:48:55 +02:00
for ( my $ i = $ y + 1 ; $ i < $ self - > { N_X } ; $ i + + ) {
if ( ( $ self - > { board } - > [ $ x ] [ $ i ] eq $ self - > { player_one_horiz } && $ player )
|| ( $ self - > { board } - > [ $ x ] [ $ i ] eq $ self - > { player_two_horiz } && ! $ player ) )
{
return 0 ;
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ x ] [ $ i ] eq $ self - > { ocean }
|| $ self - > { board } - > [ $ x ] [ $ i ] eq $ self - > { player_one_miss }
|| $ self - > { board } - > [ $ x ] [ $ i ] eq $ self - > { player_two_miss } ) {
last ;
}
2020-02-15 23:38:32 +01:00
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
for ( my $ i = $ y - 1 ; $ i >= 0 ; $ i - - ) {
if ( ( $ self - > { board } - > [ $ x ] [ $ i ] eq $ self - > { player_one_horiz } && $ player )
|| ( $ self - > { board } - > [ $ x ] [ $ i ] eq $ self - > { player_two_horiz } && ! $ player ) )
{
return 0 ;
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ x ] [ $ i ] eq $ self - > { ocean }
|| $ self - > { board } - > [ $ x ] [ $ i ] eq $ self - > { player_one_miss }
|| $ self - > { board } - > [ $ x ] [ $ i ] eq $ self - > { player_two_miss } )
{
last ;
}
2020-02-15 23:38:32 +01:00
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
return 1 ;
2018-07-01 12:07:44 +02:00
}
}
}
sub bomb {
2020-02-15 23:38:32 +01:00
my ( $ self , $ player , $ location ) = @ _ ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
my ( $ hit , $ sections , $ sunk ) = ( 0 , 0 , 0 , 0 , 0 ) ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
my ( $ x , $ y ) = $ location =~ /^(.)(.*)/ ;
2020-02-15 23:38:32 +01:00
$ x = ord ( $ x ) - 65 ;
$ y - - ;
2018-07-01 12:07:44 +02:00
if ( ! $ player ) {
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ x ] [ $ y ] eq $ self - > { player_two_vert }
|| $ self - > { board } - > [ $ x ] [ $ y ] eq $ self - > { player_two_horiz } )
{
$ hit = 1 ;
}
2018-07-01 12:07:44 +02:00
} else {
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ x ] [ $ y ] eq $ self - > { player_one_vert }
|| $ self - > { board } - > [ $ x ] [ $ y ] eq $ self - > { player_one_horiz } )
{
$ hit = 1 ;
}
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
$ sunk = $ self - > check_sunk ( $ x , $ y , $ player ) ;
if ( $ hit ) {
2021-06-29 23:48:55 +02:00
if ( ! $ player ) {
$ self - > { board } - > [ $ x ] [ $ y ] = $ self - > { player_one_hit } ;
} else {
$ self - > { board } - > [ $ x ] [ $ y ] = $ self - > { player_two_hit } ;
}
2020-02-15 23:38:32 +01:00
$ self - > { player } - > [ $ player ] - > { hit } + + ;
} else {
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ x ] [ $ y ] eq $ self - > { ocean } ) {
if ( ! $ player ) {
$ self - > { board } - > [ $ x ] [ $ y ] = $ self - > { player_one_miss } ;
} else {
$ self - > { board } - > [ $ x ] [ $ y ] = $ self - > { player_two_miss } ;
}
2020-02-15 23:38:32 +01:00
$ self - > { player } - > [ $ player ] - > { miss } + + ;
}
2018-07-01 12:07:44 +02:00
}
2021-06-29 23:48:55 +02:00
my $ nick1 = $ self - > { player } - > [ $ player ] - > { nick } ;
my $ nick2 = $ self - > { player } - > [ ! $ player ] - > { nick } ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
my @ attacks = (
2021-06-29 23:48:55 +02:00
"launches torpedoes at" ,
"launches nukes at" ,
"fires cannons at" ,
"fires torpedoes at" ,
"fires nukes at" ,
"launches tomahawk missiles at" ,
"fires a gatling gun at" ,
"launches ballistic missiles at" ,
2020-02-15 23:38:32 +01:00
) ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
my $ attacked = $ attacks [ rand @ attacks ] ;
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
if ( $ hit ) {
$ self - > send_message ( $ self - > { channel } , "$nick1 $attacked $nick2 at $location! $color{red}--- HIT! --- $color{reset}" ) ;
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { player } - > [ $ player ] - > { destroyed } + + ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
if ( $ sunk ) {
$ self - > { player } - > [ $ player ] - > { sunk } + + ;
my $ remaining = $ self - > count_ship_sections ( $ player ) ;
$ self - > send_message ( $ self - > { channel } , "$color{red}$nick1 has sunk ${nick2}'s ship! $remaining ship section" . ( $ remaining != 1 ? 's' : '' ) . " remaining!$color{reset}" ) ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
if ( $ remaining == 0 ) {
$ self - > send_message ( $ self - > { channel } , "$nick1 has WON the game of Battleship!" ) ;
$ self - > { player } - > [ $ player ] - > { won } = 1 ;
}
}
} else {
$ self - > send_message ( $ self - > { channel } , "$nick1 $attacked $nick2 at $location! --- miss ---" ) ;
2018-07-01 12:07:44 +02:00
}
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { player } - > [ $ player ] - > { bombs } + + ;
return 1 ;
2018-07-01 12:07:44 +02:00
}
2018-07-05 23:46:53 +02:00
sub show_scoreboard {
2020-02-15 23:38:32 +01:00
my ( $ self ) = @ _ ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
my $ p1sections = $ self - > count_ship_sections ( 1 ) ;
my $ p2sections = $ self - > count_ship_sections ( 0 ) ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
my $ p1win = '' ;
my $ p2win = '' ;
2019-06-26 18:34:19 +02:00
2020-02-15 23:38:32 +01:00
if ( $ p1sections > $ p2sections ) {
$ p1win = "$color{bold}$color{lightgreen} * " ;
$ p2win = "$color{red} " ;
} elsif ( $ p1sections < $ p2sections ) {
$ p1win = "$color{red} " ;
$ p2win = "$color{bold}$color{lightgreen} * " ;
2018-07-02 02:01:18 +02:00
}
2020-02-15 23:38:32 +01:00
my $ length_a = length $ self - > { player } - > [ 0 ] - > { nick } ;
my $ length_b = length $ self - > { player } - > [ 1 ] - > { nick } ;
my $ longest = $ length_a > $ length_b ? $ length_a : $ length_b ;
my $ bombslen = ( $ self - > { player } - > [ 0 ] - > { bombs } > 10 || $ self - > { player } - > [ 1 ] - > { bombs } > 10 ) ? 2 : 1 ;
2021-06-29 23:48:55 +02:00
my $ hitlen = ( $ self - > { player } - > [ 0 ] - > { hit } > 10 || $ self - > { player } - > [ 1 ] - > { hit } > 10 ) ? 2 : 1 ;
my $ misslen = ( $ self - > { player } - > [ 0 ] - > { miss } > 10 || $ self - > { player } - > [ 1 ] - > { miss } > 10 ) ? 2 : 1 ;
my $ sunklen = ( $ self - > { player } - > [ 0 ] - > { sunk } > 10 || $ self - > { player } - > [ 1 ] - > { sunk } > 10 ) ? 2 : 1 ;
my $ intactlen = ( $ p1sections > 10 || $ p2sections > 10 ) ? 2 : 1 ;
2020-02-15 23:38:32 +01:00
my $ p1bombscolor = $ self - > { player } - > [ 0 ] - > { bombs } > $ self - > { player } - > [ 1 ] - > { bombs } ? $ color { green } : $ color { red } ;
2021-06-29 23:48:55 +02:00
my $ p1hitcolor = $ self - > { player } - > [ 0 ] - > { hit } > $ self - > { player } - > [ 1 ] - > { hit } ? $ color { green } : $ color { red } ;
my $ p1misscolor = $ self - > { player } - > [ 0 ] - > { miss } < $ self - > { player } - > [ 1 ] - > { miss } ? $ color { green } : $ color { red } ;
my $ p1sunkcolor = $ self - > { player } - > [ 0 ] - > { sunk } > $ self - > { player } - > [ 1 ] - > { sunk } ? $ color { green } : $ color { red } ;
my $ p1intactcolor = $ p1sections > $ p2sections ? $ color { green } : $ color { red } ;
2020-02-15 23:38:32 +01:00
my $ p2bombscolor = $ self - > { player } - > [ 0 ] - > { bombs } < $ self - > { player } - > [ 1 ] - > { bombs } ? $ color { green } : $ color { red } ;
2021-06-29 23:48:55 +02:00
my $ p2hitcolor = $ self - > { player } - > [ 0 ] - > { hit } < $ self - > { player } - > [ 1 ] - > { hit } ? $ color { green } : $ color { red } ;
my $ p2misscolor = $ self - > { player } - > [ 0 ] - > { miss } > $ self - > { player } - > [ 1 ] - > { miss } ? $ color { green } : $ color { red } ;
my $ p2sunkcolor = $ self - > { player } - > [ 0 ] - > { sunk } < $ self - > { player } - > [ 1 ] - > { sunk } ? $ color { green } : $ color { red } ;
my $ p2intactcolor = $ p1sections < $ p2sections ? $ color { green } : $ color { red } ;
my $ buf ;
2020-02-15 23:38:32 +01:00
$ buf = sprintf (
"$p1win%*s$color{reset}: bomb: $p1bombscolor%*d$color{reset}, hit: $p1hitcolor%*d$color{reset}, miss: $p1misscolor%*d$color{reset}, sunk: $p1sunkcolor%*d$color{reset}, sections left: $p1intactcolor%*d$color{reset}" ,
$ longest , $ self - > { player } - > [ 0 ] - > { nick } , $ bombslen , $ self - > { player } - > [ 0 ] - > { bombs } ,
$ hitlen , $ self - > { player } - > [ 0 ] - > { hit } , $ misslen , $ self - > { player } - > [ 0 ] - > { miss } ,
$ sunklen , $ self - > { player } - > [ 0 ] - > { sunk } , $ intactlen , $ p1sections
) ;
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
$ self - > send_message ( $ self - > { channel } , $ buf ) ;
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
$ buf = sprintf (
"$p2win%*s$color{reset}: bomb: $p2bombscolor%*d$color{reset}, hit: $p2hitcolor%*d$color{reset}, miss: $p2misscolor%*d$color{reset}, sunk: $p2sunkcolor%*d$color{reset}, sections left: $p2intactcolor%*d$color{reset}" ,
$ longest , $ self - > { player } - > [ 1 ] - > { nick } , $ bombslen , $ self - > { player } - > [ 1 ] - > { bombs } ,
$ hitlen , $ self - > { player } - > [ 1 ] - > { hit } , $ misslen , $ self - > { player } - > [ 1 ] - > { miss } ,
$ sunklen , $ self - > { player } - > [ 1 ] - > { sunk } , $ intactlen , $ p2sections
) ;
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
$ self - > send_message ( $ self - > { channel } , $ buf ) ;
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
sub show_battlefield {
my ( $ self , $ player , $ nick ) = @ _ ;
2021-06-29 23:48:55 +02:00
$ self - > { pbot } - > { logger } - > log ( "Showing battlefield for player $player\n" ) ;
2020-02-15 23:38:32 +01:00
2021-06-29 23:48:55 +02:00
my $ buf = "$color{cyan},01 " ;
2020-02-15 23:38:32 +01:00
2021-06-29 23:48:55 +02:00
for ( my $ x = 1 ; $ x < $ self - > { N_X } + 1 ; $ x + + ) {
2020-02-15 23:38:32 +01:00
if ( $ x % 10 == 0 ) {
$ buf . = "$color{yellow},01" if $ self - > { N_X } > 10 ;
$ buf . = $ x % 10 ;
$ buf . = ' ' ;
$ buf . = "$color{cyan},01" if $ self - > { N_X } > 10 ;
2018-07-01 12:07:44 +02:00
} else {
2020-02-15 23:38:32 +01:00
$ buf . = $ x % 10 ;
$ buf . = ' ' ;
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
}
$ buf . = "\n" ;
2021-06-29 23:48:55 +02:00
for ( my $ y = 0 ; $ y < $ self - > { N_Y } ; $ y + + ) {
2020-02-15 23:38:32 +01:00
$ buf . = sprintf ( "$color{cyan},01%c " , 97 + $ y ) ;
2021-06-29 23:48:55 +02:00
for ( my $ x = 0 ; $ x < $ self - > { N_X } ; $ x + + ) {
2020-02-15 23:38:32 +01:00
if ( $ player == 0 ) {
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_two_vert }
|| $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_two_horiz } )
{
$ buf . = "$color{blue},01$self->{ocean} " ;
2020-02-15 23:38:32 +01:00
next ;
} else {
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_one_hit }
|| $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_two_hit } )
{
$ buf . = "$color{red},01" ;
}
elsif ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_two_miss }
|| $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_one_miss } )
{
$ buf . = "$color{cyan},01" ;
}
elsif ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { ocean } )
{
$ buf . = "$color{blue},01$self->{ocean} " ;
2020-02-15 23:38:32 +01:00
next ;
} else {
$ buf . = "$color{white},01" ;
}
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
$ buf . = "$self->{board}->[$y][$x] " ;
}
} elsif ( $ player == 1 ) {
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_one_vert }
|| $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_one_horiz } )
{
$ buf . = "$color{blue},01$self->{ocean} " ;
2020-02-15 23:38:32 +01:00
next ;
} else {
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_one_hit }
|| $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_two_hit } )
{
$ buf . = "$color{red},01" ;
}
elsif ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_two_miss }
|| $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_one_miss } )
{
$ buf . = "$color{cyan},01" ;
}
elsif ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { ocean } )
{
$ buf . = "$color{blue},01$self->{ocean} " ;
2020-02-15 23:38:32 +01:00
next ;
} else {
$ buf . = "$color{white},01" ;
}
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
$ buf . = "$self->{board}->[$y][$x] " ;
}
} elsif ( $ player == 2 ) {
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_one_vert }
2020-02-15 23:38:32 +01:00
|| $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_one_horiz }
|| $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_two_vert }
|| $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_two_horiz } )
{
2021-06-29 23:48:55 +02:00
$ buf . = "$color{blue},01$self->{ocean} " ;
2020-02-15 23:38:32 +01:00
next ;
} else {
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_one_hit }
|| $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_two_hit } )
{
$ buf . = "$color{red},01" ;
}
elsif ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_two_miss }
|| $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_one_miss } )
{
$ buf . = "$color{cyan},01" ;
}
elsif ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { ocean } )
{
$ buf . = "$color{blue},01$self->{ocean} " ;
2020-02-15 23:38:32 +01:00
next ;
} else {
$ buf . = "$color{white},01" ;
}
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
$ buf . = "$self->{board}->[$y][$x] " ;
}
} else {
2021-06-29 23:48:55 +02:00
if ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_one_hit }
|| $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_two_hit } )
{
$ buf . = "$color{red},01" ;
}
elsif ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_two_miss }
|| $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_one_miss } )
{
$ buf . = "$color{cyan},01" ;
}
elsif ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { ocean } )
{
$ buf . = "$color{blue},01$self->{ocean} " ;
2020-02-15 23:38:32 +01:00
next ;
} else {
$ buf . = "$color{white},01" ;
}
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
$ buf . = "$self->{board}->[$y][$x] " ;
}
2018-07-01 12:07:44 +02:00
}
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
$ buf . = sprintf ( "$color{cyan},01%c" , 97 + $ y ) ;
$ buf . = "$color{reset}\n" ;
}
# bottom border
$ buf . = "$color{cyan},01 " ;
2021-06-29 23:48:55 +02:00
for ( my $ x = 1 ; $ x < $ self - > { N_X } + 1 ; $ x + + ) {
2020-02-15 23:38:32 +01:00
if ( $ x % 10 == 0 ) {
$ buf . = $ color { yellow } , 01 if $ self - > { N_X } > 10 ;
$ buf . = $ x % 10 ;
$ buf . = ' ' ;
$ buf . = $ color { cyan } , 01 if $ self - > { N_X } > 10 ;
2018-07-01 12:07:44 +02:00
} else {
2020-02-15 23:38:32 +01:00
$ buf . = $ x % 10 ;
$ buf . = ' ' ;
2018-07-01 12:07:44 +02:00
}
}
2020-02-15 23:38:32 +01:00
$ buf . = "\n" ;
my $ player1 = $ self - > { player } - > [ 0 ] - > { nick } ;
my $ player2 = $ self - > { player } - > [ 1 ] - > { nick } ;
if ( $ player == 0 ) {
$ self - > send_message (
$ self - > { player } - > [ $ player ] - > { nick } ,
2021-06-29 23:48:55 +02:00
"Player One Legend: ships: [$self->{player_one_vert} $self->{player_one_horiz}] ocean: [$color{blue},01$self->{ocean}$color{reset}] $player1 miss: [$color{cyan},01$self->{player_one_miss}$color{reset}] $player2 miss: [$color{cyan},01$self->{player_two_miss}$color{reset}] $player1 hit: [$color{red},01"
. $ self - > { player_one_hit }
. "$color{reset}] $player2 hit: [$color{red},01$self->{player_two_hit}$color{reset}]"
2020-02-15 23:38:32 +01:00
) ;
} elsif ( $ player == 1 ) {
$ self - > send_message (
$ self - > { player } - > [ $ player ] - > { nick } ,
2021-06-29 23:48:55 +02:00
"Player Two Legend: ships: [$self->{player_two_vert} $self->{player_two_horiz}] ocean: [$color{blue},01$self->{ocean}$color{reset}] $player1 miss: [$color{cyan},01$self->{player_one_miss}$color{reset}] $player2 miss: [$color{cyan},01$self->{player_two_miss}$color{reset}] $player1 hit: [$color{red},01"
. $ self - > { player_one_hit }
. "$color{reset}] $player2 hit: [$color{red},01$self->{player_two_hit}$color{reset}]"
2020-02-15 23:38:32 +01:00
) ;
} elsif ( $ player == 2 ) {
$ self - > send_message (
$ self - > { channel } ,
2021-06-29 23:48:55 +02:00
"Spectator Legend: ocean: [$color{blue},01$self->{ocean}$color{reset}] $player1 miss: [$color{cyan},01$self->{player_one_miss}$color{reset}] $player2 miss: [$color{cyan},01$self->{player_two_miss}$color{reset}] $player1 hit: [$color{red},01"
. $ self - > { player_one_hit }
. "$color{reset}] $player2 hit: [$color{red},01$self->{player_two_hit}$color{reset}]"
2020-02-15 23:38:32 +01:00
) ;
} elsif ( $ player == 3 ) {
$ self - > send_message (
$ self - > { channel } ,
2021-06-29 23:48:55 +02:00
"Final Board Legend: $player1 ships: [$self->{player_one_vert} $self->{player_one_horiz}] $player2 ships: [$self->{player_two_vert} $self->{player_two_horiz}] ocean: [$color{blue},01$self->{ocean}$color{reset}] $player1 miss: [$color{cyan},01$self->{player_one_miss}$color{reset}] $player2 miss: [$color{cyan},01$self->{player_two_miss}$color{reset}] $player1 hit: [$color{red},01"
. $ self - > { player_one_hit }
. "$color{reset}] $player2 hit: [$color{red},01$self->{player_two_hit}$color{reset}]"
2020-02-15 23:38:32 +01:00
) ;
2018-07-06 00:34:40 +02:00
} else {
2020-02-15 23:38:32 +01:00
$ self - > send_message (
$ nick ,
2021-06-29 23:48:55 +02:00
"Full Board Legend: $player1 ships: [$self->{player_one_vert} $self->{player_one_horiz}] $player2 ships: [$self->{player_two_vert} $self->{player_two_horiz}] ocean: [$color{blue},01$self->{ocean}$color{reset}] $player1 miss: [$color{cyan},01$self->{player_one_miss}$color{reset}] $player2 miss: [$color{cyan},01$self->{player_two_miss}$color{reset}] $player1 hit: [$color{red},01"
. $ self - > { player_one_hit }
. "$color{reset}] $player2 hit: [$color{red},01$self->{player_two_hit}$color{reset}]"
2020-02-15 23:38:32 +01:00
) ;
2018-07-06 00:34:40 +02:00
}
2020-02-15 23:38:32 +01:00
foreach my $ line ( split /\n/ , $ buf ) {
2021-06-29 23:48:55 +02:00
if ( $ player == 0 || $ player == 1 ) {
$ self - > send_message ( $ self - > { player } - > [ $ player ] - > { nick } , $ line ) ;
}
elsif ( $ player == 2 || $ player == 3 ) {
$ self - > send_message ( $ self - > { channel } , $ line ) ;
}
else {
$ self - > send_message ( $ nick , $ line ) ;
}
2018-07-01 12:07:44 +02:00
}
}
2021-06-29 23:48:55 +02:00
# game state machine stuff
# do one loop of the game engine
sub run_one_state {
my ( $ self ) = @ _ ;
# check for naughty or missing players
for ( my $ i = 0 ; $ i < @ { $ self - > { state_data } - > { players } } ; $ i + + ) {
if ( $ self - > { state_data } - > { players } - > [ $ i ] - > { missedinputs } >= 3 ) {
# remove player if they have missed 3 inputs
$ self - > send_message (
$ self - > { channel } ,
"$color{red}$self->{state_data}->{players}->[$i]->{name} has missed too many prompts and has been ejected from the game!$color{reset}"
) ;
$ self - > { state_data } - > { players } - > [ $ i ] - > { removed } = 1 ;
}
if ( $ self - > { state_data } - > { players } - > [ $ i ] - > { removed } ) {
# end game if a player has been removed
$ self - > set_state ( 'gameover' ) ;
last ;
}
}
# transitioned to a brand new state; prepare first tock
if ( $ self - > { previous_state } ne $ self - > { current_state } ) {
$ self - > { state_data } - > { ticks } = 1 ;
$ self - > { state_data } - > { first_tock } = 1 ;
$ self - > { state_data } - > { tocks } = 0 ;
# dump new state data for logging/debugging
if ( $ self - > { debug } ) {
$ Data:: Dumper:: Useqq = 1 ;
$ Data:: Dumper:: Sortkeys = 1 ;
$ self - > { pbot } - > { logger } - > log ( "Battleship: New state: $self->{current_state}\n" . Dumper $ self - > { state_data } ) ;
}
}
# run one state/tick
$ self - > { states } - > { $ self - > { current_state } } - > { sub } - > ( $ self - > { state_data } ) ;
# transition to next state
$ self - > { previous_state } = $ self - > { current_state } ;
$ self - > { current_state } = $ self - > { states } - > { $ self - > { current_state } } - > { trans } - > { $ self - > { state_data } - > { trans } } ;
# reset tick-tock once we've tocked
if ( $ self - > { state_data } - > { tocked } ) {
$ self - > { state_data } - > { tocked } = 0 ;
$ self - > { state_data } - > { ticks } = 0 ;
$ self - > { state_data } - > { first_tock } = 0 ;
}
# next tick
$ self - > { state_data } - > { ticks } + + ;
}
# skip directly to a state
sub set_state {
my ( $ self , $ newstate ) = @ _ ;
$ self - > { previous_state } = $ self - > { current_state } ;
$ self - > { current_state } = $ newstate ;
$ self - > { state_data } - > { ticks } = 0 ;
}
# set up game state machine
sub create_states {
my ( $ self ) = @ _ ;
$ self - > { pbot } - > { logger } - > log ( "Battleship: Creating game state machine\n" ) ;
# initialize default state
$ self - > { previous_state } = '' ;
$ self - > { current_state } = 'nogame' ;
# initialize state data
$ self - > { state_data } = {
players = > [] , # array of player data
ticks = > 0 , # number of ticks elapsed
current_player = > 0 , # whose turn is it?
} ;
$ self - > { states } = {
nogame = > {
sub => sub { $ self - > nogame ( @ _ ) } ,
trans = > {
challenge = > 'accept' ,
nogame = > 'nogame' ,
}
} ,
accept = > {
sub => sub { $ self - > accept ( @ _ ) } ,
trans = > {
stop = > 'nogame' ,
wait = > 'accept' ,
accept = > 'genboard' ,
}
} ,
genboard = > {
sub => sub { $ self - > genboard ( @ _ ) } ,
trans = > {
next = > 'showboard' ,
}
} ,
showboard = > {
sub => sub { $ self - > showboard ( @ _ ) } ,
trans = > {
next = > 'playermove' ,
}
} ,
playermove = > {
sub => sub { $ self - > playermove ( @ _ ) } ,
trans = > {
wait = > 'playermove' ,
next = > 'checkplayer' ,
}
} ,
checkplayer = > {
sub => sub { $ self - > checkplayer ( @ _ ) } ,
trans = > {
gotwinner = > 'gameover' ,
next = > 'playermove' ,
}
} ,
gameover = > {
sub => sub { $ self - > gameover ( @ _ ) } ,
trans = > {
wait = > 'gameover' ,
next = > 'nogame' ,
}
} ,
} ;
}
# game states
2018-07-01 12:07:44 +02:00
sub nogame {
2020-02-15 23:38:32 +01:00
my ( $ self , $ state ) = @ _ ;
2021-06-29 23:48:55 +02:00
$ state - > { trans } = 'nogame' ;
2021-06-22 02:26:24 +02:00
$ self - > { pbot } - > { event_queue } - > update_repeating ( 'battleship loop' , 0 ) ;
2018-07-01 12:07:44 +02:00
}
sub accept {
2020-02-15 23:38:32 +01:00
my ( $ self , $ state ) = @ _ ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
$ state - > { tock_limit } = 3 ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
if ( $ state - > { players } - > [ 1 ] - > { accepted } ) {
2021-06-29 23:48:55 +02:00
$ state - > { trans } = 'accept' ;
return ;
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
my $ tock = 15 ;
if ( $ state - > { ticks } % $ tock == 0 ) {
$ state - > { tocked } = 1 ;
2021-06-29 23:48:55 +02:00
if ( + + $ state - > { tocks } > $ state - > { tock_limit } ) {
2020-02-15 23:38:32 +01:00
if ( $ state - > { players } - > [ 1 ] - > { id } == - 1 ) { $ self - > send_message ( $ self - > { channel } , "Nobody has accepted $state->{players}->[0]->{name}'s challenge." ) ; }
else { $ self - > send_message ( $ self - > { channel } , "$state->{players}->[1]->{name} has failed to accept $state->{players}->[0]->{name}'s challenge." ) ; }
2021-06-29 23:48:55 +02:00
$ state - > { trans } = 'stop' ;
2020-02-15 23:38:32 +01:00
$ state - > { players } = [] ;
2021-06-29 23:48:55 +02:00
return ;
2020-02-15 23:38:32 +01:00
}
if ( $ state - > { players } - > [ 1 ] - > { id } == - 1 ) {
$ self - > send_message ( $ self - > { channel } , "$state->{players}->[0]->{name} has made an open challenge! Use `accept` to accept their challenge." ) ;
} else {
$ self - > send_message ( $ self - > { channel } , "$state->{players}->[1]->{name}: $state->{players}->[0]->{name} has challenged you! Use `accept` to accept their challenge." ) ;
}
2018-07-02 03:46:58 +02:00
}
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
$ state - > { trans } = 'wait' ;
2018-07-01 12:07:44 +02:00
}
sub genboard {
2020-02-15 23:38:32 +01:00
my ( $ self , $ state ) = @ _ ;
$ self - > init_game ( $ state - > { players } - > [ 0 ] - > { name } , $ state - > { players } - > [ 1 ] - > { name } ) ;
$ state - > { current_player } = 0 ;
2021-06-29 23:48:55 +02:00
$ state - > { tock_limit } = 3 ;
$ state - > { trans } = 'next' ;
2018-07-01 12:07:44 +02:00
}
sub showboard {
2020-02-15 23:38:32 +01:00
my ( $ self , $ state ) = @ _ ;
$ self - > send_message ( $ self - > { channel } , "Showing battlefield to $self->{player}->[0]->{nick}..." ) ;
$ self - > show_battlefield ( 0 ) ;
$ self - > send_message ( $ self - > { channel } , "Showing battlefield to $self->{player}->[1]->{nick}..." ) ;
$ self - > show_battlefield ( 1 ) ;
$ self - > send_message ( $ self - > { channel } , "Fight! Anybody (players and spectators) can use `board` at any time to see the battlefield." ) ;
2021-06-29 23:48:55 +02:00
$ state - > { trans } = 'next' ;
2018-07-01 12:07:44 +02:00
}
2018-07-02 05:39:55 +02:00
sub playermove {
2020-02-15 23:38:32 +01:00
my ( $ self , $ state ) = @ _ ;
2018-07-01 12:07:44 +02:00
2021-06-29 23:48:55 +02:00
my $ tock = 15 ;
if ( $ state - > { first_tock } ) {
$ tock = 3 ;
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
if ( $ self - > { player } - > [ $ state - > { current_player } ] - > { done } ) {
2021-06-29 23:48:55 +02:00
$ state - > { trans } = 'next' ;
return ;
2018-07-03 08:33:22 +02:00
}
2020-02-15 23:38:32 +01:00
my $ player = $ state - > { current_player } ;
my $ location = delete $ state - > { players } - > [ $ player ] - > { location } ;
if ( defined $ location ) {
2021-06-29 23:48:55 +02:00
if ( $ self - > bomb ( $ player , $ location ) ) {
2020-02-15 23:38:32 +01:00
$ self - > { player } - > [ $ player ] - > { done } = 1 ;
$ self - > { player } - > [ ! $ player ] - > { done } = 0 ;
$ self - > { state_data } - > { current_player } = ! $ player ;
2021-06-29 23:48:55 +02:00
$ state - > { trans } = 'next' ;
return ;
2020-02-15 23:38:32 +01:00
}
2018-07-02 05:39:55 +02:00
}
2020-02-15 23:38:32 +01:00
if ( $ state - > { ticks } % $ tock == 0 ) {
$ state - > { tocked } = 1 ;
2021-06-29 23:48:55 +02:00
if ( + + $ state - > { tocks } > $ state - > { tock_limit } ) {
2020-02-15 23:38:32 +01:00
$ state - > { players } - > [ $ state - > { current_player } ] - > { missedinputs } + + ;
$ self - > send_message ( $ self - > { channel } , "$state->{players}->[$state->{current_player}]->{name} failed to launch an attack in time. They forfeit their turn!" ) ;
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { player } - > [ $ state - > { current_player } ] - > { done } = 1 ;
$ self - > { player } - > [ ! $ state - > { current_player } ] - > { done } = 0 ;
$ state - > { current_player } = ! $ state - > { current_player } ;
2021-06-29 23:48:55 +02:00
$ state - > { trans } = 'next' ;
return ;
2020-02-15 23:38:32 +01:00
}
2018-07-02 05:39:55 +02:00
2021-06-29 23:48:55 +02:00
my $ red = $ state - > { tocks } == $ state - > { tock_limit } ? $ color { red } : '' ;
2018-07-02 05:39:55 +02:00
2021-06-29 23:48:55 +02:00
my $ remaining = 15 * $ state - > { tock_limit } ;
$ remaining -= 15 * ( $ state - > { tocks } - 1 ) ;
$ remaining = "(" . ( concise duration $ remaining ) . " remaining)" ;
2020-02-15 23:38:32 +01:00
$ self - > send_message ( $ self - > { channel } , "$state->{players}->[$state->{current_player}]->{name}: $red$remaining Launch an attack now via `bomb <location>`!$color{reset}" ) ;
}
2018-07-02 05:39:55 +02:00
2021-06-29 23:48:55 +02:00
$ state - > { trans } = 'wait' ;
2018-07-01 12:07:44 +02:00
}
2018-07-02 05:39:55 +02:00
sub checkplayer {
2020-02-15 23:38:32 +01:00
my ( $ self , $ state ) = @ _ ;
2018-07-02 05:39:55 +02:00
2021-06-29 23:48:55 +02:00
if ( $ self - > { player } - > [ 0 ] - > { won } or $ self - > { player } - > [ 1 ] - > { won } ) {
$ state - > { trans } = 'gotwinner' ;
} else {
$ state - > { trans } = 'next' ;
}
2018-07-01 12:07:44 +02:00
}
sub gameover {
2020-02-15 23:38:32 +01:00
my ( $ self , $ state ) = @ _ ;
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
if ( $ state - > { ticks } % 5 == 0 ) {
if ( $ state - > { players } - > [ 1 ] - > { id } != - 1 && $ state - > { players } - > [ 1 ] - > { accepted } ) {
$ self - > show_battlefield ( 3 ) ;
$ self - > show_scoreboard ;
$ self - > send_message ( $ self - > { channel } , "Game over!" ) ;
}
2021-06-29 23:48:55 +02:00
2020-02-15 23:38:32 +01:00
$ state - > { players } = [] ;
2021-06-29 23:48:55 +02:00
$ state - > { tocks } = 0 ;
$ state - > { trans } = 'next' ;
2020-02-15 23:38:32 +01:00
} else {
2021-06-29 23:48:55 +02:00
$ state - > { trans } = 'wait' ;
2018-07-03 08:33:22 +02:00
}
2018-07-01 12:07:44 +02:00
}
1 ;