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
use Time::Duration qw/concise duration/ ;
2018-07-02 04:43:27 +02:00
use Data::Dumper ;
2020-02-15 23:38:32 +01:00
$ Data:: Dumper:: Useqq = 1 ;
2018-07-02 04:43:27 +02:00
$ Data:: Dumper:: Sortkeys = 1 ;
2018-07-01 12:07:44 +02:00
sub initialize {
2020-02-15 23:38:32 +01:00
my ( $ self , % conf ) = @ _ ;
2020-05-04 22:21:35 +02:00
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_battleship ( @ _ ) } , 'battleship' , 0 ) ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { event_dispatcher } - > register_handler ( 'irc.part' , sub { $ self - > on_departure ( @ _ ) } ) ;
$ self - > { pbot } - > { event_dispatcher } - > register_handler ( 'irc.quit' , sub { $ self - > on_departure ( @ _ ) } ) ;
$ self - > { pbot } - > { event_dispatcher } - > register_handler ( 'irc.kick' , sub { $ self - > on_kick ( @ _ ) } ) ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { channel } = $ self - > { pbot } - > { registry } - > get_value ( 'battleship' , 'channel' ) // '##battleship' ;
$ self - > { debug } = $ self - > { pbot } - > { registry } - > get_value ( 'battleship' , 'debug' ) // 0 ;
2020-01-22 06:03:47 +01:00
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 } = '=' ;
2020-01-22 06:03:47 +01:00
2020-02-15 23:38:32 +01:00
$ self - > create_states ;
2018-07-01 12:07:44 +02:00
}
sub unload {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
$ self - > { pbot } - > { commands } - > unregister ( 'battleship' ) ;
2020-03-08 04:27:15 +01:00
$ self - > { pbot } - > { timer } - > dequeue_event ( 'battleship loop' ) ;
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { event_dispatcher } - > remove_handler ( 'irc.part' ) ;
$ self - > { pbot } - > { event_dispatcher } - > remove_handler ( 'irc.quit' ) ;
$ self - > { pbot } - > { event_dispatcher } - > remove_handler ( 'irc.kick' ) ;
2018-07-01 12:07:44 +02:00
}
sub on_kick {
2020-02-15 23:38:32 +01:00
my ( $ self , $ event_type , $ event ) = @ _ ;
2020-05-04 22:21:35 +02:00
my ( $ nick , $ user , $ host ) = ( $ event - > { event } - > nick , $ event - > { event } - > user , $ event - > { event } - > host ) ;
2020-02-15 23:38:32 +01:00
my ( $ victim , $ reason ) = ( $ event - > { event } - > to , $ event - > { event } - > { args } [ 1 ] ) ;
my $ channel = $ event - > { event } - > { args } [ 0 ] ;
return 0 if lc $ channel ne $ self - > { channel } ;
$ self - > player_left ( $ nick , $ user , $ host ) ;
return 0 ;
2018-07-01 12:07:44 +02:00
}
sub on_departure {
2020-02-15 23:38:32 +01:00
my ( $ self , $ event_type , $ event ) = @ _ ;
my ( $ nick , $ user , $ host , $ channel ) = ( $ event - > { event } - > nick , $ event - > { event } - > user , $ event - > { event } - > host , $ event - > { event } - > to ) ;
my $ type = uc $ event - > { event } - > type ;
return 0 if $ type ne 'QUIT' and lc $ channel ne $ self - > { channel } ;
$ self - > player_left ( $ nick , $ user , $ host ) ;
return 0 ;
2018-07-01 12:07:44 +02:00
}
my % color = (
2020-02-15 23:38:32 +01:00
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" ,
bold = > "\x02" ,
italics = > "\x1D" ,
underline = > "\x1F" ,
reverse = > "\x16" ,
reset = > "\x0F" ,
2018-07-01 12:07:44 +02:00
) ;
2020-05-04 22:21:35 +02:00
sub cmd_battleship {
my ( $ self , $ context ) = @ _ ;
$ context - > { arguments } =~ s/^\s+|\s+$//g ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
my $ usage = "Usage: battleship challenge|accept|bomb|board|score|quit|players|kick|abort; for more information about a command: battleship help <command>" ;
2018-07-01 12:07:44 +02:00
2020-05-04 22:21:35 +02:00
my ( $ command , $ arguments ) = split / / , $ context - > { arguments } , 2 ;
2020-02-15 23:38:32 +01:00
$ command = lc $ command ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
my ( $ channel , $ result ) ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
given ( $ command ) {
2018-07-01 12:07:44 +02:00
when ( 'help' ) {
2020-02-15 23:38:32 +01:00
given ( $ arguments ) {
when ( 'help' ) { return "Seriously?" ; }
default {
if ( length $ arguments ) { return "Battleship help is coming soon." ; }
else { return "Usage: battleship help <command>" ; }
}
}
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
when ( 'leaderboard' ) { return "Coming soon." ; }
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
when ( 'challenge' ) {
if ( $ self - > { current_state } ne 'nogame' ) { return "There is already a game of Battleship underway." ; }
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
if ( not length $ arguments ) {
$ self - > { current_state } = 'accept' ;
$ self - > { state_data } = { players = > [] , counter = > 0 } ;
2018-07-01 12:07:44 +02:00
2020-05-04 22:21:35 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account ( $ context - > { nick } , $ context - > { user } , $ context - > { host } ) ;
my $ player = { id = > $ id , name = > $ context - > { nick } , missedinputs = > 0 } ;
2020-02-15 23:38:32 +01:00
push @ { $ self - > { state_data } - > { players } } , $ player ;
2018-07-02 03:46:58 +02:00
2020-02-15 23:38:32 +01:00
$ player = { id = > - 1 , name = > undef , missedinputs = > 0 } ;
push @ { $ self - > { state_data } - > { players } } , $ player ;
2020-03-08 04:27:15 +01:00
$ self - > { pbot } - > { timer } - > enqueue_event ( sub {
$ self - > run_one_state ;
} , 1 , 'battleship loop' , 1
) ;
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $context->{nick} has made an open challenge! Use `accept` to accept their challenge." ;
2020-02-15 23:38:32 +01:00
}
2018-07-02 03:46:58 +02:00
2020-02-15 23:38:32 +01:00
my $ challengee = $ self - > { pbot } - > { nicklist } - > is_present ( $ self - > { channel } , $ arguments ) ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
if ( not $ challengee ) { return "That nick is not present in this channel. Invite them to $self->{channel} and try again!" ; }
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { current_state } = 'accept' ;
$ self - > { state_data } = { players = > [] , counter = > 0 } ;
2018-07-01 12:07:44 +02:00
2020-05-04 22:21:35 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account ( $ context - > { nick } , $ context - > { user } , $ context - > { host } ) ;
my $ player = { id = > $ id , name = > $ context - > { nick } , missedinputs = > 0 } ;
2020-02-15 23:38:32 +01:00
push @ { $ self - > { state_data } - > { players } } , $ player ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
( $ id ) = $ self - > { pbot } - > { messagehistory } - > { database } - > find_message_account_by_nick ( $ challengee ) ;
$ player = { id = > $ id , name = > $ challengee , missedinputs = > 0 } ;
push @ { $ self - > { state_data } - > { players } } , $ player ;
2018-07-01 12:07:44 +02:00
2020-03-08 04:27:15 +01:00
$ self - > { pbot } - > { timer } - > enqueue_event ( sub {
$ self - > battleship_loop ;
} , 1 , 'battleship loop' , 1
) ;
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $context->{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
2020-02-15 23:38:32 +01:00
when ( 'accept' ) {
2020-05-04 22:21:35 +02:00
if ( $ self - > { current_state } ne 'accept' ) { return "/msg $context->{nick} This is not the time to use `accept`." ; }
2018-07-01 12:07:44 +02:00
2020-05-04 22:21:35 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account ( $ context - > { nick } , $ context - > { user } , $ context - > { host } ) ;
2020-02-15 23:38:32 +01:00
my $ player = $ self - > { state_data } - > { players } - > [ 1 ] ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
# open challenge
if ( $ player - > { id } == - 1 ) {
$ player - > { id } = $ id ;
2020-05-04 22:21:35 +02:00
$ player - > { name } = $ context - > { 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
if ( $ player - > { id } == $ id ) {
$ player - > { accepted } = 1 ;
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $context->{nick} has accepted $self->{state_data}->{players}->[0]->{name}'s challenge!" ;
2020-02-15 23:38:32 +01:00
} else {
2020-05-04 22:21:35 +02:00
return "/msg $context->{nick} You have not been challenged to a game of Battleship yet." ;
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 ( $ _ eq 'decline' or $ _ eq 'quit' or $ _ eq 'forfeit' or $ _ eq 'concede' ) {
2020-05-04 22:21:35 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account ( $ context - > { nick } , $ context - > { user } , $ context - > { 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 ) {
if ( $ self - > { state_data } - > { current_player } >= @ { $ self - > { state_data } - > { players } } ) { $ self - > { state_data } - > { current_player } = @ { $ self - > { state_data } - > { players } } - 1 }
if ( @ { $ self - > { state_data } - > { players } } == 2 and ( $ self - > { state_data } - > { players } - > [ 1 ] - > { id } == - 1 || not $ self - > { state_data } - > { players } - > [ 1 ] - > { accepted } ) ) {
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $context->{nick} declined the challenge." ;
2020-02-15 23:38:32 +01:00
} else {
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $context->{nick} has left the game!" ;
2020-02-15 23:38:32 +01:00
}
} else {
2020-05-04 22:21:35 +02:00
return "$context->{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' ) {
2020-05-04 22:21:35 +02:00
if ( not $ self - > { pbot } - > { users } - > loggedin_admin ( $ self - > { channel } , $ context - > { hostmask } ) ) {
return "$context->{nick}: Only admins may abort the game." ;
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { current_state } = 'gameover' ;
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $context->{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 ;
return ;
} 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' ) {
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 `battleship challenge <nick>`!" ; }
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
when ( 'kick' ) {
2020-05-04 22:21:35 +02:00
if ( not $ self - > { pbot } - > { users } - > loggedin_admin ( $ self - > { channel } , $ context - > { hostmask } ) ) {
return "$context->{nick}: Only admins may kick people from the game." ;
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01: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 ) {
if ( $ self - > { state_data } - > { current_player } >= @ { $ self - > { state_data } - > { players } } ) { $ self - > { state_data } - > { current_player } = @ { $ self - > { state_data } - > { players } } - 1 }
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $context->{nick}: $arguments has been kicked from the game." ;
2020-02-15 23:38:32 +01:00
} else {
2020-05-04 22:21:35 +02:00
return "$context->{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' ) {
if ( $ self - > { debug } ) { $ self - > { pbot } - > { logger } - > log ( "Battleship: bomb state: $self->{current_state}\n" . Dumper $ self - > { state_data } ) ; }
2018-07-01 12:07:44 +02:00
2020-05-04 22:21:35 +02:00
if ( $ self - > { current_state } ne 'playermove' and $ self - > { current_state } ne 'checkplayer' ) { return "$context->{nick}: It's not time to do that now." ; }
2018-07-03 08:33:22 +02:00
2020-05-04 22:21:35 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account ( $ context - > { nick } , $ context - > { user } , $ context - > { host } ) ;
2020-02-15 23:38:32 +01:00
my $ player ;
2018-07-03 08:33:22 +02:00
2020-02-15 23:38:32 +01: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
2020-02-15 23:38:32 +01:00
if ( not length $ arguments ) {
2020-05-04 22:21:35 +02:00
if ( delete $ self - > { state_data } - > { players } - > [ $ player ] - > { location } ) { return "$context->{nick}: Attack location cleared." ; }
else { return "$context->{nick}: Usage: bomb <location>" ; }
2020-02-15 23:38:32 +01:00
}
2018-07-03 08:33:22 +02:00
2020-05-04 22:21:35 +02:00
if ( $ arguments !~ m/^[a-zA-Z][0-9]+$/ ) { return "$context->{nick}: Usage: battleship bomb <location>; <location> must be in the form of A15, B3, C9, etc." ; }
2018-07-03 08:33:22 +02:00
2020-02-15 23:38:32 +01:00
$ arguments = uc $ arguments ;
2018-07-03 08:33:22 +02:00
2020-02-15 23:38:32 +01:00
my ( $ x , $ y ) ;
( $ x ) = $ arguments =~ m/^(.)/ ;
( $ 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
2020-05-04 22:21:35 +02:00
if ( $ x < 0 || $ x > $ self - > { N_Y } || $ y < 0 || $ y > $ self - > { N_X } ) { return "$context->{nick}: Target out of range, try again." ; }
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
if ( $ self - > { state_data } - > { current_player } != $ player ) {
my $ msg ;
2020-05-04 22:21:35 +02:00
if ( not exists $ self - > { state_data } - > { players } - > [ $ player ] - > { location } ) { $ msg = "$context->{nick}: You will attack $arguments when it is your turn." ; }
else { $ msg = "$context->{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
2020-05-04 22:21:35 +02:00
if ( $ self - > { player } - > [ $ player ] - > { done } ) { return "$context->{nick}: You have already attacked this turn." ; }
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
if ( $ self - > bomb ( $ player , uc $ arguments ) ) {
if ( $ self - > { player } - > [ $ player ] - > { won } ) {
$ self - > { previous_state } = $ self - > { current_state } ;
$ self - > { current_state } = 'checkplayer' ;
$ self - > run_one_state ;
} else {
$ self - > { player } - > [ $ player ] - > { done } = 1 ;
$ self - > { player } - > [ ! $ player ] - > { done } = 0 ;
$ self - > { state_data } - > { current_player } = ! $ player ;
$ self - > { state_data } - > { ticks } = 1 ;
$ self - > { state_data } - > { first_tock } = 1 ;
$ self - > { state_data } - > { counter } = 0 ;
}
}
}
2019-06-26 18:34:19 +02:00
2020-02-15 23:38:32 +01:00
when ( $ _ eq 'specboard' or $ _ eq 'board' ) {
if ( $ self - > { current_state } eq 'nogame' or $ self - > { current_state } eq 'accept' or $ self - > { current_state } eq 'genboard' or $ self - > { current_state } eq 'gameover' ) {
2020-05-04 22:21:35 +02:00
return "$context->{nick}: There is no board to show right now." ;
2020-02-15 23:38:32 +01:00
}
if ( $ _ eq 'specboard' ) {
$ self - > show_battlefield ( 2 ) ;
return ;
}
2020-05-04 22:21:35 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account ( $ context - > { nick } , $ context - > { user } , $ context - > { host } ) ;
2020-02-15 23:38:32 +01:00
for ( my $ i = 0 ; $ i < 2 ; $ i + + ) {
if ( $ self - > { state_data } - > { players } - > [ $ i ] - > { id } == $ id ) {
2020-05-04 22:21:35 +02:00
$ self - > send_message ( $ self - > { channel } , "$context->{nick} surveys the battlefield!" ) ;
2020-02-15 23:38:32 +01:00
$ self - > show_battlefield ( $ i ) ;
return ;
}
}
$ self - > show_battlefield ( 2 ) ;
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
when ( 'fullboard' ) {
2020-05-04 22:21:35 +02:00
if ( not $ self - > { pbot } - > { users } - > loggedin_admin ( $ self - > { channel } , $ context - > { hostmask } ) ) {
return "$context->{nick}: Only admins may see the full board." ;
}
2020-02-15 23:38:32 +01:00
if ( $ self - > { current_state } eq 'nogame' or $ self - > { current_state } eq 'accept' or $ self - > { current_state } eq 'genboard' or $ self - > { current_state } eq 'gameover' ) {
2020-05-04 22:21:35 +02:00
return "$context->{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!
2020-05-04 22:21:35 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account ( $ context - > { nick } , $ context - > { user } , $ context - > { host } ) ;
2020-02-15 23:38:32 +01:00
for ( my $ i = 0 ; $ i < 2 ; $ i + + ) {
if ( $ self - > { state_data } - > { players } - > [ $ i ] - > { id } == $ id ) {
2020-05-04 22:21:35 +02:00
$ self - > send_message ( $ self - > { channel } , "$context->{nick} surveys the battlefield!" ) ;
2020-02-15 23:38:32 +01:00
$ self - > show_battlefield ( $ i ) ;
return ;
}
}
2020-05-04 22:21:35 +02:00
$ self - > show_battlefield ( 4 , $ context - > { nick } ) ;
2018-07-01 12:07:44 +02:00
}
2019-06-26 18:34:19 +02:00
2020-02-15 23:38:32 +01:00
default { return $ usage ; }
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
return $ result ;
2018-07-01 12:07:44 +02:00
}
sub player_left {
2020-02-15 23:38:32 +01:00
my ( $ self , $ nick , $ user , $ host ) = @ _ ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account ( $ nick , $ user , $ host ) ;
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 ( $ self - > { state_data } - > { players } - > [ $ i ] - > { id } == $ id ) {
$ self - > { state_data } - > { players } - > [ $ i ] - > { removed } = 1 ;
$ self - > send_message ( $ self - > { channel } , "$nick has left the game!" ) ;
$ removed = 1 ;
}
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
if ( $ removed ) {
if ( $ self - > { state_data } - > { current_player } >= @ { $ self - > { state_data } - > { players } } ) { $ self - > { state_data } - > { current_player } = @ { $ self - > { state_data } - > { players } } - 1 }
return "/msg $self->{channel} $nick has left the game!" ;
2018-07-01 12:07:44 +02:00
}
}
sub send_message {
2020-02-15 23:38:32 +01:00
my ( $ self , $ to , $ text , $ delay ) = @ _ ;
$ delay = 0 if not defined $ delay ;
my $ botnick = $ self - > { pbot } - > { registry } - > get_value ( 'irc' , 'botnick' ) ;
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
} ;
$ self - > { pbot } - > { interpreter } - > add_message_to_output_queue ( $ to , $ message , $ delay ) ;
2018-07-01 12:07:44 +02:00
}
sub run_one_state {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
# check for naughty or missing players
if ( $ self - > { current_state } =~ /(?:move|accept)/ ) {
my $ removed = 0 ;
for ( my $ i = 0 ; $ i < @ { $ self - > { state_data } - > { players } } ; $ i + + ) {
if ( $ self - > { state_data } - > { players } - > [ $ i ] - > { missedinputs } >= 3 ) {
$ 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 ;
$ removed = 1 ;
}
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
if ( $ removed ) {
if ( $ self - > { state_data } - > { current_player } >= @ { $ self - > { state_data } - > { players } } ) { $ self - > { state_data } - > { current_player } = @ { $ self - > { state_data } - > { players } } - 1 }
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
if ( $ self - > { state_data } - > { players } - > [ 0 ] - > { removed } or $ self - > { state_data } - > { players } - > [ 1 ] - > { removed } ) { $ self - > { current_state } = 'gameover' ; }
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
my $ state_data = $ self - > { state_data } ;
# this shouldn't happen
if ( not defined $ self - > { current_state } ) {
$ self - > { pbot } - > { logger } - > log ( "Battleship state broke.\n" ) ;
$ self - > { current_state } = 'nogame' ;
return ;
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
# transistioned to a brand new state; prepare first tock
if ( $ self - > { previous_state } ne $ self - > { current_state } ) {
$ state_data - > { newstate } = 1 ;
$ state_data - > { ticks } = 1 ;
if ( exists $ state_data - > { tick_drift } ) {
$ state_data - > { ticks } += $ state_data - > { tick_drift } ;
delete $ state_data - > { tick_drift } ;
}
$ state_data - > { first_tock } = 1 ;
$ state_data - > { counter } = 0 ;
} else {
$ state_data - > { newstate } = 0 ;
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
# dump new state data for logging/debugging
if ( $ self - > { debug } and $ state_data - > { newstate } ) { $ self - > { pbot } - > { logger } - > log ( "Battleship: New state: $self->{current_state}\n" . Dumper $ state_data ) ; }
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
# run one state/tick
$ state_data = $ self - > { states } { $ self - > { current_state } } { sub } ( $ state_data ) ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
if ( $ state_data - > { tocked } ) {
delete $ state_data - > { tocked } ;
delete $ state_data - > { first_tock } ;
$ state_data - > { ticks } = 0 ;
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
# transform to next state
$ state_data - > { previous_result } = $ state_data - > { result } ;
$ self - > { previous_state } = $ self - > { current_state } ;
$ self - > { current_state } = $ self - > { states } { $ self - > { current_state } } { trans } { $ state_data - > { result } } ;
$ self - > { state_data } = $ state_data ;
# next tick
$ self - > { state_data } - > { ticks } + + ;
2018-07-01 12:07:44 +02:00
}
sub create_states {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "Battleship: Creating game state machine\n" ) ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { previous_state } = '' ;
$ self - > { current_state } = 'nogame' ;
$ self - > { state_data } = { players = > [] , ticks = > 0 , newstate = > 1 } ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { state_data } - > { current_player } = 0 ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { states } { 'nogame' } { sub } = sub { $ self - > nogame ( @ _ ) } ;
$ self - > { states } { 'nogame' } { trans } { challenge } = 'accept' ;
$ self - > { states } { 'nogame' } { trans } { nogame } = 'nogame' ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { states } { 'accept' } { sub } = sub { $ self - > accept ( @ _ ) } ;
$ self - > { states } { 'accept' } { trans } { stop } = 'nogame' ;
$ self - > { states } { 'accept' } { trans } { wait } = 'accept' ;
$ self - > { states } { 'accept' } { trans } { accept } = 'genboard' ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { states } { 'genboard' } { sub } = sub { $ self - > genboard ( @ _ ) } ;
$ self - > { states } { 'genboard' } { trans } { next } = 'showboard' ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { states } { 'showboard' } { sub } = sub { $ self - > showboard ( @ _ ) } ;
$ self - > { states } { 'showboard' } { trans } { next } = 'playermove' ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { states } { 'playermove' } { sub } = sub { $ self - > playermove ( @ _ ) } ;
$ self - > { states } { 'playermove' } { trans } { wait } = 'playermove' ;
$ self - > { states } { 'playermove' } { trans } { next } = 'checkplayer' ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { states } { 'checkplayer' } { sub } = sub { $ self - > checkplayer ( @ _ ) } ;
$ self - > { states } { 'checkplayer' } { trans } { sunk } = 'gameover' ;
$ self - > { states } { 'checkplayer' } { trans } { next } = 'playermove' ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { states } { 'gameover' } { sub } = sub { $ self - > gameover ( @ _ ) } ;
$ self - > { states } { 'gameover' } { trans } { wait } = 'gameover' ;
$ self - > { states } { 'gameover' } { trans } { next } = 'nogame' ;
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
2020-02-15 23:38:32 +01:00
for ( my $ x = 0 ; $ x < $ self - > { SHIPS } ; $ x + + ) { $ self - > { ship_length } - > [ $ x ] = 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 } = [
{ bombs = > 0 , hit = > 0 , miss = > 0 , sunk = > 0 , nick = > $ nick1 , done = > 0 } ,
{ bombs = > 0 , hit = > 0 , miss = > 0 , sunk = > 0 , nick = > $ nick2 , done = > 0 }
] ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { turn } = 0 ;
$ 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 ) = @ _ ;
my ( $ x , $ y , $ sections ) ;
$ sections = 0 ;
for ( $ x = 0 ; $ x < $ self - > { N_Y } ; $ x + + ) {
for ( $ y = 0 ; $ y < $ self - > { N_X } ; $ y + + ) {
if ( $ player == 0 ) {
if ( $ self - > { board } - > [ $ x ] [ $ y ] eq $ self - > { player_two_vert } || $ self - > { board } - > [ $ x ] [ $ y ] eq $ self - > { player_two_horiz } ) { $ sections + + ; }
} else {
if ( $ self - > { board } - > [ $ x ] [ $ y ] eq $ self - > { player_one_vert } || $ self - > { board } - > [ $ x ] [ $ y ] eq $ self - > { player_one_horiz } ) { $ sections + + ; }
}
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 ) = @ _ ;
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 ;
if ( $ y - $ l < 0 ) { return 0 ; }
} else {
$ yd = 1 ;
if ( $ y + $ l >= $ self - > { N_X } ) { return 0 ; }
}
$ xd = 0 ;
2018-07-01 12:07:44 +02:00
} else {
2020-02-15 23:38:32 +01:00
if ( ! $ d ) {
$ xd = - 1 ;
if ( $ x - $ l < 0 ) { return 0 ; }
} else {
$ xd = 1 ;
if ( $ x + $ l >= $ self - > { N_Y } ) { return 0 ; }
}
$ 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 + + ) {
if ( $ self - > { board } - > [ $ x += $ o ? $ xd : 0 ] [ $ y += $ o ? 0 : $ yd ] ne '~' ) { 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 ) = @ _ ;
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 ) = @ _ ;
my ( $ x , $ y , $ o , $ d , $ i , $ l ) ;
my ( $ yd , $ xd ) = ( 0 , 0 ) ;
my $ fail = 0 ;
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 ;
if ( not $ self - > { ship_length } - > [ $ ship ] ) { $ l = $ self - > number ( 3 , 6 ) ; }
else { $ l = $ self - > { ship_length } - > [ $ ship ] ; }
$ self - > { pbot } - > { logger } - > log ( "generate ships player $player: ship $ship x,y: $x,$y o,d: $o,$d length: $l\n" ) ;
if ( $ self - > check_ship ( $ x , $ y , $ o , $ d , $ l ) ) {
if ( ! $ o ) {
if ( $ self - > { horiz } < 2 ) { next ; }
if ( ! $ d ) { $ yd = - 1 ; }
else { $ yd = 1 ; }
$ xd = 0 ;
} else {
$ self - > { horiz } + + ;
if ( ! $ d ) { $ xd = - 1 ; }
else { $ xd = 1 ; }
$ 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." ) ;
$ self - > { current_state } = 'nogame' ;
return 0 ;
}
2018-07-01 12:07:44 +02:00
}
}
sub generate_battlefield {
2020-02-15 23:38:32 +01:00
my ( $ self ) = @ _ ;
my ( $ x , $ y ) ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
for ( $ y = 0 ; $ y < $ self - > { N_Y } ; $ y + + ) {
for ( $ x = 0 ; $ x < $ self - > { N_X } ; $ x + + ) { $ self - > { board } - > [ $ y ] [ $ x ] = '~' ; }
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
for ( $ 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
}
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 ) = @ _ ;
my ( $ i , $ target ) ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ 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 } ) {
for ( $ 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
2020-02-15 23:38:32 +01:00
if ( $ self - > { board } - > [ $ i ] [ $ y ] eq '~' || $ self - > { board } - > [ $ i ] [ $ y ] eq '*' || $ self - > { board } - > [ $ i ] [ $ y ] eq 'o' ) { last ; }
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
for ( $ 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 ; }
if ( $ self - > { board } - > [ $ i ] [ $ y ] eq '~' || $ self - > { board } - > [ $ i ] [ $ y ] eq '*' || $ self - > { board } - > [ $ i ] [ $ y ] eq 'o' ) { last ; }
}
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 } ) {
for ( $ 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
2020-02-15 23:38:32 +01:00
if ( $ self - > { board } - > [ $ x ] [ $ i ] eq '~' || $ self - > { board } - > [ $ x ] [ $ i ] eq '*' || $ self - > { board } - > [ $ x ] [ $ i ] eq 'o' ) { last ; }
}
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
for ( $ 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
2020-02-15 23:38:32 +01:00
if ( $ self - > { board } - > [ $ x ] [ $ i ] eq '~' || $ self - > { board } - > [ $ x ] [ $ i ] eq '*' || $ self - > { board } - > [ $ x ] [ $ i ] eq 'o' ) { last ; }
}
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 ) = @ _ ;
my ( $ x , $ y , $ hit , $ sections , $ sunk ) = ( 0 , 0 , 0 , 0 , 0 ) ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ location = uc $ location ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
( $ x ) = $ location =~ m/^(.)/ ;
( $ y ) = $ location =~ m/^.(.*)/ ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ x = ord ( $ x ) - 65 ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "bomb player $player $x,$y $self->{board}->[$x][$y]\n" ) ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
if ( $ x < 0 || $ x > $ self - > { N_Y } || $ y < 0 || $ y > $ self - > { N_X } ) {
$ self - > send_message ( $ self - > { channel } , "Target out of range, try again." ) ;
return 0 ;
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
$ y - - ;
2018-07-01 12:07:44 +02:00
if ( ! $ player ) {
2020-02-15 23:38:32 +01: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 {
2020-02-15 23:38:32 +01: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 ) {
if ( ! $ player ) { $ self - > { board } - > [ $ x ] [ $ y ] = '1' ; }
else { $ self - > { board } - > [ $ x ] [ $ y ] = '2' ; }
$ self - > { player } - > [ $ player ] - > { hit } + + ;
} else {
if ( $ self - > { board } - > [ $ x ] [ $ y ] eq '~' ) {
if ( ! $ player ) { $ self - > { board } - > [ $ x ] [ $ y ] = '*' ; }
else { $ self - > { board } - > [ $ x ] [ $ y ] = 'o' ; }
$ self - > { player } - > [ $ player ] - > { miss } + + ;
}
2018-07-01 12:07:44 +02:00
}
2020-02-15 23:38:32 +01:00
my $ nick1 = $ self - > { player } - > [ $ player ] - > { nick } ;
my $ nick2 = $ self - > { player } - > [ $ player ? 0 : 1 ] - > { nick } ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
my @ attacks = (
"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"
) ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
my $ attacked = $ attacks [ rand @ attacks ] ;
if ( $ hit ) {
$ self - > send_message ( $ self - > { channel } , "$nick1 $attacked $nick2 at $location! $color{red}--- HIT! --- $color{reset}" ) ;
$ 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
}
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 $ buf ;
my $ p1sections = $ self - > count_ship_sections ( 1 ) ;
my $ p2sections = $ self - > count_ship_sections ( 0 ) ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01: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 ;
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 ;
my $ p1bombscolor = $ self - > { player } - > [ 0 ] - > { bombs } > $ self - > { player } - > [ 1 ] - > { bombs } ? $ color { green } : $ color { red } ;
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 } ;
my $ p2bombscolor = $ self - > { player } - > [ 0 ] - > { bombs } < $ self - > { player } - > [ 1 ] - > { bombs } ? $ color { green } : $ color { red } ;
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 } ;
$ 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
) ;
$ self - > send_message ( $ self - > { channel } , $ buf ) ;
$ 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
) ;
$ 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 ) = @ _ ;
my ( $ x , $ y , $ buf ) ;
$ self - > { pbot } - > { logger } - > log ( "showing battlefield for player $player\n" ) ;
$ buf = "$color{cyan},01 " ;
for ( $ x = 1 ; $ x < $ self - > { N_X } + 1 ; $ x + + ) {
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" ;
for ( $ y = 0 ; $ y < $ self - > { N_Y } ; $ y + + ) {
$ buf . = sprintf ( "$color{cyan},01%c " , 97 + $ y ) ;
for ( $ x = 0 ; $ x < $ self - > { N_X } ; $ x + + ) {
if ( $ player == 0 ) {
if ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_two_vert } || $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_two_horiz } ) {
$ buf . = "$color{blue},01~ " ;
next ;
} else {
if ( $ self - > { board } - > [ $ y ] [ $ x ] eq '1' || $ self - > { board } - > [ $ y ] [ $ x ] eq '2' ) { $ buf . = "$color{red},01" ; }
elsif ( $ self - > { board } - > [ $ y ] [ $ x ] eq 'o' || $ self - > { board } - > [ $ y ] [ $ x ] eq '*' ) { $ buf . = "$color{cyan},01" ; }
elsif ( $ self - > { board } - > [ $ y ] [ $ x ] eq '~' ) {
$ buf . = "$color{blue},01~ " ;
next ;
} else {
$ buf . = "$color{white},01" ;
}
$ buf . = "$self->{board}->[$y][$x] " ;
$ self - > { pbot } - > { logger } - > log ( "$y, $x: $self->{board}->[$y][$x]\n" ) ;
}
} elsif ( $ player == 1 ) {
if ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_one_vert } || $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_one_horiz } ) {
$ buf . = "$color{blue},01~ " ;
next ;
} else {
if ( $ self - > { board } - > [ $ y ] [ $ x ] eq '1' || $ self - > { board } - > [ $ y ] [ $ x ] eq '2' ) { $ buf . = "$color{red},01" ; }
elsif ( $ self - > { board } - > [ $ y ] [ $ x ] eq 'o' || $ self - > { board } - > [ $ y ] [ $ x ] eq '*' ) { $ buf . = "$color{cyan},01" ; }
elsif ( $ self - > { board } - > [ $ y ] [ $ x ] eq '~' ) {
$ buf . = "$color{blue},01~ " ;
next ;
} else {
$ buf . = "$color{white},01" ;
}
$ buf . = "$self->{board}->[$y][$x] " ;
}
} elsif ( $ player == 2 ) {
if ( $ self - > { board } - > [ $ y ] [ $ x ] eq $ self - > { player_one_vert }
|| $ 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 } )
{
$ buf . = "$color{blue},01~ " ;
next ;
} else {
if ( $ self - > { board } - > [ $ y ] [ $ x ] eq '1' || $ self - > { board } - > [ $ y ] [ $ x ] eq '2' ) { $ buf . = "$color{red},01" ; }
elsif ( $ self - > { board } - > [ $ y ] [ $ x ] eq 'o' || $ self - > { board } - > [ $ y ] [ $ x ] eq '*' ) { $ buf . = "$color{cyan},01" ; }
elsif ( $ self - > { board } - > [ $ y ] [ $ x ] eq '~' ) {
$ buf . = "$color{blue},01~ " ;
next ;
} else {
$ buf . = "$color{white},01" ;
}
$ buf . = "$self->{board}->[$y][$x] " ;
}
} else {
if ( $ self - > { board } - > [ $ y ] [ $ x ] eq '1' || $ self - > { board } - > [ $ y ] [ $ x ] eq '2' ) { $ buf . = "$color{red},01" ; }
elsif ( $ self - > { board } - > [ $ y ] [ $ x ] eq 'o' || $ self - > { board } - > [ $ y ] [ $ x ] eq '*' ) { $ buf . = "$color{cyan},01" ; }
elsif ( $ self - > { board } - > [ $ y ] [ $ x ] eq '~' ) {
$ buf . = "$color{blue},01~ " ;
next ;
} else {
$ buf . = "$color{white},01" ;
}
$ buf . = "$self->{board}->[$y][$x] " ;
}
2018-07-01 12:07:44 +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 " ;
for ( $ x = 1 ; $ x < $ self - > { N_X } + 1 ; $ x + + ) {
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 } ,
"Player One Legend: ships: [| -] ocean: [$color{blue},01~$color{reset}] $player1 miss: [$color{cyan},01*$color{reset}] $player2 miss: [$color{cyan},01o$color{reset}] $player1 hit: [$color{red},01"
. "1"
. "$color{reset}] $player2 hit: [$color{red},012$color{reset}]"
) ;
} elsif ( $ player == 1 ) {
$ self - > send_message (
$ self - > { player } - > [ $ player ] - > { nick } ,
"Player Two Legend: ships: [I =] ocean: [$color{blue},01~$color{reset}] $player1 miss: [$color{cyan},01*$color{reset}] $player2 miss: [$color{cyan},01o$color{reset}] $player1 hit: [$color{red},01"
. "1"
. "$color{reset}] $player2 hit: [$color{red},012$color{reset}]"
) ;
} elsif ( $ player == 2 ) {
$ self - > send_message (
$ self - > { channel } ,
"Spectator Legend: ocean: [$color{blue},01~$color{reset}] $player1 miss: [$color{cyan},01*$color{reset}] $player2 miss: [$color{cyan},01o$color{reset}] $player1 hit: [$color{red},01"
. "1"
. "$color{reset}] $player2 hit: [$color{red},012$color{reset}]"
) ;
} elsif ( $ player == 3 ) {
$ self - > send_message (
$ self - > { channel } ,
"Final Board Legend: $player1 ships: [| -] $player2 ships: [I =] ocean: [$color{blue},01~$color{reset}] $player1 miss: [$color{cyan},01*$color{reset}] $player2 miss: [$color{cyan},01o$color{reset}] $player1 hit: [$color{red},01"
. "1"
. "$color{reset}] $player2 hit: [$color{red},012$color{reset}]"
) ;
2018-07-06 00:34:40 +02:00
} else {
2020-02-15 23:38:32 +01:00
$ self - > send_message (
$ nick ,
"Full Board Legend: $player1 ships: [| -] $player2 ships: [I =] ocean: [$color{blue},01~$color{reset}] $player1 miss: [$color{cyan},01*$color{reset}] $player2 miss: [$color{cyan},01o$color{reset}] $player1 hit: [$color{red},01"
. "1"
. "$color{reset}] $player2 hit: [$color{red},012$color{reset}]"
) ;
2018-07-06 00:34:40 +02:00
}
2020-02-15 23:38:32 +01:00
foreach my $ line ( split /\n/ , $ buf ) {
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
}
}
# state subroutines
sub nogame {
2020-02-15 23:38:32 +01:00
my ( $ self , $ state ) = @ _ ;
$ state - > { result } = 'nogame' ;
2020-03-08 04:27:15 +01:00
$ self - > { pbot } - > { timer } - > update_repeating ( 'battleship loop' , 0 ) ;
2020-02-15 23:38:32 +01:00
return $ state ;
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
2020-02-15 23:38:32 +01:00
$ state - > { max_count } = 3 ;
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
if ( $ state - > { players } - > [ 1 ] - > { accepted } ) {
$ state - > { result } = 'accept' ;
return $ state ;
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 ;
if ( + + $ state - > { counter } > $ state - > { max_count } ) {
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." ) ; }
$ state - > { result } = 'stop' ;
$ state - > { players } = [] ;
return $ state ;
}
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
2020-02-15 23:38:32 +01:00
$ state - > { result } = 'wait' ;
return $ state ;
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 ;
$ state - > { max_count } = 3 ;
$ state - > { result } = 'next' ;
return $ state ;
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." ) ;
$ state - > { result } = 'next' ;
return $ state ;
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
2020-02-15 23:38:32 +01:00
my $ tock ;
if ( $ state - > { first_tock } ) { $ tock = 3 ; }
else { $ tock = 15 ; }
2018-07-01 12:07:44 +02:00
2020-02-15 23:38:32 +01:00
if ( $ self - > { player } - > [ $ state - > { current_player } ] - > { done } ) {
$ self - > { pbot } - > { logger } - > log ( "playermove: player $state->{current_player} done, nexting\n" ) ;
$ state - > { result } = 'next' ;
return $ state ;
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 ) {
if ( $ self - > bomb ( $ player , uc $ location ) ) {
$ self - > { player } - > [ $ player ] - > { done } = 1 ;
$ self - > { player } - > [ ! $ player ] - > { done } = 0 ;
$ self - > { state_data } - > { current_player } = ! $ player ;
$ state - > { result } = 'next' ;
return $ state ;
}
2018-07-02 05:39:55 +02:00
}
2020-02-15 23:38:32 +01:00
if ( $ state - > { ticks } % $ tock == 0 ) {
$ state - > { tocked } = 1 ;
if ( + + $ state - > { counter } > $ state - > { max_count } ) {
$ 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!" ) ;
$ self - > { player } - > [ $ state - > { current_player } ] - > { done } = 1 ;
$ self - > { player } - > [ ! $ state - > { current_player } ] - > { done } = 0 ;
$ state - > { current_player } = ! $ state - > { current_player } ;
$ state - > { result } = 'next' ;
return $ state ;
}
2018-07-02 05:39:55 +02:00
2020-02-15 23:38:32 +01:00
my $ red = $ state - > { counter } == $ state - > { max_count } ? $ color { red } : '' ;
2018-07-02 05:39:55 +02:00
2020-02-15 23:38:32 +01:00
my $ remaining = 15 * $ state - > { max_count } ;
$ remaining -= 15 * ( $ state - > { counter } - 1 ) ;
$ remaining = "(" . ( concise duration $ remaining ) . " remaining)" ;
$ 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
2020-02-15 23:38:32 +01:00
$ state - > { result } = 'wait' ;
return $ state ;
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
2020-02-15 23:38:32 +01:00
if ( $ self - > { player } - > [ 0 ] - > { won } or $ self - > { player } - > [ 1 ] - > { won } ) { $ state - > { result } = 'sunk' ; }
else { $ state - > { result } = 'next' ; }
return $ state ;
2018-07-01 12:07:44 +02:00
}
sub gameover {
2020-02-15 23:38:32 +01:00
my ( $ self , $ state ) = @ _ ;
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!" ) ;
}
$ state - > { players } = [] ;
$ state - > { counter } = 0 ;
$ state - > { result } = 'next' ;
} else {
$ state - > { result } = 'wait' ;
2018-07-03 08:33:22 +02:00
}
2020-02-15 23:38:32 +01:00
return $ state ;
2018-07-01 12:07:44 +02:00
}
1 ;