# Judge Program # note CHANGE FORMAT TIME TO AVOID TRUCATION at lines 345 and 346 # updated 2006.06.22 @ 01:04:28 PM make text windows scrolled, see end of text area use strict ; use Data::Dump 'dump'; # simple procedural interface $, ='<-->' ; # print between elements in an array $\ ="\n" ; # eliminate need for "\n" at end of line use Tk ; use Cwd ; use Time::HiRes qw( gettimeofday ); my %special_chars = ( braceleft => '{', braceright => '}', bracketleft => '[', bracketright => ']', parenleft => '(', parenright => ')', space => ' ', comma => ',', period => '.', greater => '>', less => '<', slash => '/', backslash => '\\', bar => '|', quotedbl => '"', quoteright => "'", Tab => "\t", equal => '=', underscore => '_', plus => '+', minus => '-', exclam => '!', at => '@', numbersign => '#', dollar => '$', percent => '%', asterisk => '*', asciicircum => '^', asciitilde => '~', quoteleft => '`', ampersand => '&', Return => "\n", colon => ":", semicolon => ";", question => "?", BackSpace => "BackSpace", ) ; my ( $program_number, $judge , $human, %start_flag , $logwindow, # Log window %logwindow, # widgets for $logwindow %text_window, # ROText to display logs @received_characters, # Array containing directory names from glob() $num_rcvd_chars, # Number of elements in @received_characters %log, # %log is the transcript of the session character-by-character %processed, # array containing characters received indexex by time component of name # used make sure that if a directory is re-globbed because # the OS did not have time to delete, it will not be processed a second time %folder_Name, # Where to mkdir, glob() for each session @all_time_indexes, $insert_letter, # Letter to insert in window $remote_time, # Time of remote keypress capture. Used to avoid dupl characters $local_time, # Time of local keypress capture. Used as key to %log $log_lo, # lo resolution component $log_hi, # hi resolution component %note, # Array of judges notes, indexed by time, side, location $note, %note_button, # Button to invoke note %label, $log_window, $mw, $tn_window, %text_frames, $version, ) ; $version = '2.0.0' ; # session information my ( %session_info, $session_number, $home_dir, # home directory ) ; $home_dir = getcwd ; # get name of home directory # $session_info{program number}{session_number}{judge|human} $session_info{P1}{1}{judge} = 'J1' ; $session_info{P1}{2}{judge} = 'J4' ; $session_info{P1}{3}{judge} = 'J3' ; $session_info{P1}{4}{judge} = 'J2' ; $session_info{P1}{1}{human} = 'H1' ; $session_info{P1}{2}{human} = 'H2' ; $session_info{P1}{3}{human} = 'H3' ; $session_info{P1}{4}{human} = 'H4' ; $session_info{P2}{3}{judge} = 'J1' ; $session_info{P2}{5}{judge} = 'J2' ; $session_info{P2}{6}{judge} = 'J4' ; $session_info{P2}{7}{judge} = 'J3' ; $session_info{P2}{3}{human} = 'H2' ; $session_info{P2}{5}{human} = 'H1' ; $session_info{P2}{6}{human} = 'H3' ; $session_info{P2}{7}{human} = 'H4' ; $session_info{P3}{1}{judge} = 'J2' ; $session_info{P3}{2}{judge} = 'J3' ; $session_info{P3}{3}{judge} = 'J4' ; $session_info{P3}{5}{judge} = 'J1' ; $session_info{P3}{1}{human} = 'H2' ; $session_info{P3}{2}{human} = 'H1' ; $session_info{P3}{3}{human} = 'H4' ; $session_info{P3}{5}{human} = 'H3' ; $session_info{P4}{2}{judge} = 'J2' ; $session_info{P4}{4}{judge} = 'J3' ; $session_info{P4}{6}{judge} = 'J1' ; $session_info{P4}{7}{judge} = 'J4' ; $session_info{P4}{2}{human} = 'H3' ; $session_info{P4}{4}{human} = 'H2' ; $session_info{P4}{6}{human} = 'H4' ; $session_info{P4}{7}{human} = 'H1' ; $mw = MainWindow -> new() ; $mw -> bind('' => \&endprogram) ; my $dialog ; $dialog = $mw->Dialog(-text => 'Which Program?', -title => 'Enter Program', -default_button => 'P1', -buttons => [qw/P1 P2 P3 P4/]); my $scrheight = $mw -> screenheight ; my $scrwidth = $mw -> screenwidth ; $scrwidth = $scrwidth; my $screensize = ($scrwidth).'x'.($scrheight-200). '+0+0' ; $mw -> geometry($screensize) ; $text_frames{left}{remote} = $mw -> Frame( -background => 'white', -borderwidth => 4, -relief => 'groove', ) -> form(-t => '%0', -l => '%0', -b => '%45', -r => '%50') ; $note_button{left}{remote} = $text_frames{left}{remote} -> Button( -text =>" LEFT press for\nOTHER note", -font => ['Times',18,'bold'], -justify => 'center', -background => 'pink', -borderwidth => 4, -relief => 'groove', -takefocus => 0, -command => sub{Take_note(side => 'left', location => 'remote') }, ) -> form(-t => '%0', -l => '%0 ', -r => '%100 ') ; $text_window{left}{remote} = $text_frames{left}{remote} -> Scrolled('ROText', -font => ['Times',12,], # changed to scrolled -borderwidth => 4, -background => 'white', -relief => 'groove', -wrap => 'char', -takefocus => 0, ) -> form(-t => $note_button{left}{remote}, -l => '%0 ', -r => '%100 ', -b => '%100') ; $text_frames{left}{local} = $mw -> Frame( -background => 'white', -borderwidth => 4, -relief => 'groove', ) -> form(-t => '%45', -l => '%0', -b => '%90', -r => '%50') ; $label{left} = $text_frames{left}{local} -> Label( -text =>"LEFT\nME", -font => ['Times',18,'bold'], -justify => 'center', -background => 'white', ) -> form(-t => '%0 ', -l => '%0 ', -r => '%100') ; $text_window{left}{local} = $text_frames{left}{local} -> Scrolled('ROText', -font => ['Times',12,], # changed to scrolled -borderwidth => 4, -background => 'white', -relief => 'groove', -wrap => 'char', ) -> form(-t => $label{left}, -l => '%0 ', -r => '%100 ', -b => '%100') ; $text_frames{right}{remote} = $mw -> Frame( -background => 'white', -borderwidth => 4, -relief => 'groove', ) -> form(-t => '%0', -l => '%50', -b => '%45', -r => '%100') ; $note_button{right}{remote} = $text_frames{right}{remote} -> Button( -text => " RIGHT press for\nOTHER note", -font => ['Times',18,'bold'], -justify => 'center', -background => 'pink', -borderwidth => 4, -relief => 'groove', -takefocus => 0, -command => sub{Take_note(side => 'right', location => 'remote') }, ) -> form(-t => '%0 ', -l => '%0 ', -r => '%100 ') ; $text_window{right}{remote} = $text_frames{right}{remote} -> Scrolled('ROText', -font => ['Times',12,], # changed to scrolled -borderwidth => 4, -background => 'white', -relief => 'groove', -wrap => 'char', -takefocus => 0, ) -> form(-t => $note_button{right}{remote}, -l => '%0 ', -r => '%100 ', -b => '%100') ; $text_frames{right}{local} = $mw -> Frame( -background => 'white', -borderwidth => 4, -relief => 'groove', ) -> form(-t => '%45', -l => '%50', -b => '%90', -r => '%100') ; $label{right} = $text_frames{right}{local} -> Label( -text =>"RIGHT\nME", -font => ['Times',18,'bold'], -justify => 'center', -background => 'white', -borderwidth => 4, ) -> form(-t => '%0 ', -l => '%0 ', -r => '%100 ') ; $text_window{right}{local} = $text_frames{right}{local} -> Scrolled('ROText', -font => ['Times',12,], # changed to scrolled -borderwidth => 4, -background => 'white', -relief => 'groove', -wrap => 'char', -relief => 'groove', -borderwidth => 6, ) -> form(-t => $label{right}, -l => '%0 ', -r => '%100 ', -b => '%100') ; my $Session_Button = $mw -> Button( -text => "Change\nSession", -font => ['courier',24,'bold'], -background => 'light yellow', -command => sub{ Change_Session() }, -takefocus => 0, ) -> form (-t => '%90', -l => '%0', -r => '%100', -b => '%100') ; $Session_Button -> bind('', \&endprogram) ; $text_window{left}{local} -> bind('<1>' => \&Lock_Out_Right ) ; # On button press, make sure that Right window can't take focus $text_window{left}{local} -> bind('' => [\&capture_send_keypress,'left']); # On keypress, process it $text_window{right}{local} -> bind('' => [\&capture_send_keypress,'right']); $text_window{right}{local} -> bind('<1>' => \&Lock_Out_Left ); $program_number = $dialog -> Show ; my ($sec, $min, $hour, $day, $month, $year, $week_day, $day_of_year, $isdst) = localtime() ; $year += 1900 ; $month ++ ; # increment so that Jan is 01 instead of 00 $sec = sprintf '%02d', $sec ; $min = sprintf '%02d', $min ; $hour = sprintf '%02d', $hour ; $day = sprintf '%02d', $day ; $month = sprintf '%02d', $month ; open (LOGFILE, ">logfile.$program_number.$year-$month-$day--$hour-$min-$sec.log") ; print LOGFILE "Loebner Prize 20006 Sept 17, 2006 University College London" ; print LOGFILE "These transcripts are in the public domain" ; print LOGFILE "Transcripts of program $program_number" ; $session_number = 0 ; Change_Session() ; $mw -> update ; endless_loop() ; sub endless_loop{ while(1){ get_remote_char() ; if(Exists $mw){ $mw -> update } ; }; } MainLoop ; sub Change_Session{ $session_number ++ ; # skip if program is excused this session unless(exists $session_info{$program_number}{$session_number}){ $mw -> messageBox(-type => 'ok', -message => "Program $program_number is excused session during $session_number") ; $mw -> configure(-title => "Loebner Prize 2006 Judge Communications Program version $version Program $program_number excused during session $session_number") ; return; } else{ $judge = $session_info{$program_number}{$session_number}{judge} ; $human = $session_info{$program_number}{$session_number}{human} ; $mw -> configure(-title => "Loebner Prize 2006 Judge Communications Program version $version Session $session_number") ; } # clear screens foreach my $left_right_index('left', 'right'){ foreach my $local_remote_index('local','remote'){ $text_window{$left_right_index}{$local_remote_index} -> delete('0.0', 'end') ; } } # if(exists $session_info{}{} chdir $home_dir ; $folder_Name{left} = $mw -> Tk::chooseDirectory( -title => "Choose Left Directory for Program $program_number Judge $judge Human $human" ) ; $folder_Name{right} = $mw -> Tk::chooseDirectory( -title => "Choose Right Directory for Program $program_number Judge $judge Human $human" ) ; $folder_Name{left} =~ /.*\/(.*$)/ ; my $left = $1 ; $folder_Name{right} =~ /.*\/(.*$)/ ; my $right = $1 ; print LOGFILE "starting session $session_number left= $left right = $right" ; } sub Take_note { my %args = @_ ; my ($lo, $hi)= gettimeofday ; $hi = sprintf '%07u', $hi ; $lo = sprintf '%011u', $lo ; my $time = "$lo$hi" ; my %tn_args = @_ ; $tn_window = $mw -> Toplevel( -title => $tn_args{ side } .' '. $tn_args{ location } . " $time" ) ; my $tn_window_txt = $tn_window ->Scrolled('Text') -> form(-t => 120, -l => '%0 ', -r => '%100 ', -b => '%100') ; $tn_window_txt -> focus ; my $tn_window_button = $tn_window ->Button( -text => 'Save', -command => sub{ $note = $tn_window_txt -> Contents ; log_note(data => $note, side => $tn_args{side} ) ; $tn_window -> destroy ; } , ) -> form(-t => 0, -l => '%50 ', ) ; sub log_note{ my %ln_args = @_ ; while( $ln_args{data} =~ s/(.*)?\n//){ if($1){ print LOGFILE "$judge* $time $time local $ln_args{ side } $1" ; } } $note ="" ; } } sub endprogram{ close (LOGFILE) ; exit } ; sub capture_send_keypress{; # This subroutine captures a keypress # It then creates a directory whose name is the character pressed # and calls a routine to insert character in window # # capture key events my $widget = shift; my $side = shift ; if($side eq 'left'){ chdir $folder_Name{left} ; } else{ chdir $folder_Name{right} ; } my $e = $widget->XEvent ; my ($keysym_text, $keysym_decimal) = ($e->K, $e-> N) ; # Uncomment the following to see either # print $keysym_text, $keysym_decimal ; # if (length $keysym_text > 1){ # a special character, disregard unless allowed unless(exists $special_chars{$keysym_text}){ return ; # not found - don't process } } # note CHANGE FORMAT TIME TO AVOID TRUCATION my ($lo, $hi)= gettimeofday ; $hi = sprintf '%07u', $hi ; $lo = sprintf '%011u', $lo ; $local_time = $lo.$hi ; my $send_char = "$local_time.$keysym_text.judge" ; # Create directory for remote judge or program bot mkdir $send_char ; # Isert in local window insert_char( data =>$keysym_text, side => $side, source => 'local' , time => $local_time ) } ; sub insert_char{ # # This subroutine inserts the character in the local window # as well as to log hash # my ($lo, $hi)= gettimeofday ; # note that time is fomatted to prevent errors from truncated high resolution time $hi = sprintf '%07u', $hi ; $lo = sprintf '%011u', $lo ; my $log_time = "$lo$hi" ; my %arguments = @_ ; my $what_to_insert = $arguments{data} ; if (length $what_to_insert > 1){ # a special character, make null $what_to_insert = '' ; } if( $special_chars{$arguments{data} } ){ # If there is an element in the hash table, replace null with value $what_to_insert = $special_chars{$arguments{data}} ; } unless($what_to_insert eq ''){ $log{$program_number}{$session_number}{ $log_time }{ $arguments{ source } }{ $arguments{ side} } = $what_to_insert ; } elsif( $arguments{data} eq 'BackSpace' ){ $log{$program_number}{$session_number}{ $log_time }{ $arguments{ source } }{ $arguments{ side} } = 'BackSpace' ; } ; my $char ; if($what_to_insert eq ' '){ $char = 'space' } elsif($what_to_insert eq "\n"){ $char = 'CR' } else{ $char = $what_to_insert ; } if($char eq ' '){ $char = 'space' } elsif($char eq "\t"){ $char = 'tab' }; if($char){ #avoid null, which sometimes occurs - TODO find out why print LOGFILE "$judge $log_time $arguments{time} $arguments{source} $arguments{side} $char " ; } unless( $arguments{data} eq 'BackSpace'){ # Check for special case of "BackSpace" $text_window{ $arguments{side} }{ $arguments{source} } -> insert( 'end',$what_to_insert ) ; # No, insert character null or otherwise at end $text_window{ $arguments{side} }{ $arguments{source} } -> see( 'end') ; } else{ $text_window{ $arguments{side} }{ $arguments{source} } -> delete( "end -2 chars", "end -1 chars" ) ; # Yes, delete last character $text_window{ $arguments{side} }{ $arguments{source} } -> see( "end" ) ; } ; } ; # # The following two subroutines prevent a window from taking focus # This is necessary since a tab character will otherwise force the # cursor to the other window sub Lock_Out_Right{ $text_window{left}{local} -> configure( -takefocus => 1 ); $text_window{right}{local} -> configure(-takefocus => 0) ; $text_window{left}{local} -> markSet( 'insert', "end") } ; sub Lock_Out_Left{ $text_window{right}{local} -> configure( -takefocus => 1 ) ; $text_window{left}{local} -> configure( -takefocus => 0) ; $text_window{right}{local} -> markSet( 'insert', "end") ; sub get_remote_char{ # This subroutine checks for characters # First check "Left" folder foreach my $side ('left','right'){ chdir $folder_Name{$side} ; # fill array with names of directories @received_characters = glob("*.other") ; $num_rcvd_chars = scalar @received_characters ; # get number of received characters if( $num_rcvd_chars ){ foreach my $temp_char ( @received_characters ){ rmdir $temp_char ; # First, remove the directory ( $remote_time, $insert_letter, undef )= split '\.', $temp_char ; # Extract info unless(exists $processed{ $remote_time }{$side} ){ # hash element does not exits, therefore this character has not yet been processed # add element to hash indexed by time and insert letter in window $processed{ $remote_time }{$side} = $insert_letter ; insert_char(data => $insert_letter, side => $side, source => 'remote', time => $remote_time) ; } } ; @received_characters = () ; } ; } } }