#!/usr/bin/perl #***************************************************************************** # Tangobulario (a program to practice your vocabulary of foreign words) # # Copyright (c) 2008-2009 Nancho Alvarez # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . #****************************************************************************** $file="palabras.txt"; $repaso_extension=".review"; @sv=(); #secciones validas $tanda=25; $escribir_flag="yes"; $listarsecciones="no"; $exponente=1.5; $reverse="no"; $repaso="no"; &parser; if ($repaso eq "yes") {@sv=()} #in review-mode disable sections if (!open (LEER, $file)) {print "Error de lectura: $file - $!\n"; &usage; exit 1} #open(LEER, $file) || die "Error de lectura: $file\n$!"; @lines = ; close(LEER); $j=0; @preguntables=(); @s=(); #todas las secciones if (@sv==0) {$flag=1} else {$flag=0} $seccionactual=""; foreach(@lines){ if (/^\[(.+)\]$/) { $seccionactual=$1; if ($1 =~ /=/) {print "Warning: section name '$seccionactual' contains the symbol '='\n";} push(@s, $seccionactual."\n"); &activarflag; } elsif (/=/ and $flag){ push(@preguntables, $j); &subdivide; } $j++; } if ($listarsecciones eq "yes") { foreach(@s){print } exit 0; } if (@preguntables==0) {die "There are no words\n"} if ($tanda>@preguntables) {$tanda=@preguntables} @probabilidades=(); &asignarprobabilidades; @elegidos=(); if ($repaso eq "no"){ &elegir; foreach(@elegidos){ $veces[$_]++ } #increment only once } else { &leer_repaso; $tanda=@elegidos; } $answer="yes"; $preguntar_solo_falladas="no"; @falladas_primera_vez=(); $primera_vez="yes"; while ($answer eq "yes") { #bucle principal mas externo $ronda=1; if ($preguntar_solo_falladas eq "yes") { @lista=@falladas_primera_vez; $aciertos=$tanda-@falladas_primera_vez; } else { @lista=@elegidos; $aciertos=0 } while($aciertos<$tanda){ #here starts a round @falladas=(); $primera_pregunta="yes"; while(@lista){ #here starts a question $cuantos=@lista; if ($primera_pregunta eq "yes" and $cuantos > 1){ $cuantos--; # hack: the first question of each round # should be different from the last failed one } $i=int(rand $cuantos); $primera_pregunta="no"; &asignar_idiomas; print "$idioma1: "; chomp($respuesta=<>); if ($respuesta eq "=" and $escribir_flag eq "yes"){ &corregir; } elsif ($respuesta eq "=="){ $aciertos++; splice(@lista, $i, 1); print "---> $idioma2\n"; } elsif ($respuesta eq "=exit"){ &abortar; } elsif ($respuesta eq $idioma2){ $aciertos++; splice(@lista, $i, 1); } else { print "***** WRONG ******: $idioma2\n"; $fallos[$lista[$i]]++; push(@falladas, $lista[$i]); splice(@lista, $i, 1); } } my $f=@falladas; print "\n\n--- round: $ronda --- failed: $f ---\n\n\n\n"; if ($primera_vez eq "yes"){ @falladas_primera_vez=@falladas; $primera_vez="no" } $ronda++; @lista=@falladas; } $answer=&menu; } #THE END ##################################################################### sub menu{ my $repetir_menu="yes"; $preguntar_solo_falladas="no"; while ($repetir_menu eq "yes"){ print "Again (Y/n/x/s/f/h)?: "; $respuesta=<>; chomp ($respuesta); if ($respuesta =~ /^[hH]/) { print "y - again\n", "n - exit (saving statistics and review)\n", "x - exit without saving\n", "s - save statistics\n", "f - ask only failed questions\n", "h - this help\n"; print "read-only mode: and are equivalent, and has no effect\n" unless ($escribir_flag eq "yes"); print "\n"; } if ($respuesta =~ /^[xX]/) { $answer=0; $escribir_flag="no"; $repetir_menu="no"; } if ($respuesta =~ /^[nN]/) { $answer=0; $repetir_menu="no"; if ($escribir_flag eq "yes"){ &escribir; &escribir_repaso unless ($repaso eq "yes"); } } if (($respuesta =~ /^[yY]/) || ($respuesta eq "")) { $repetir_menu="no"; } if ($respuesta =~ /^[sS]/) { if ($escribir_flag eq "yes") { &escribir; print "statistics saved\n"; } else {print "no effect\n"} } if ($respuesta =~ /^[fF]/) { if (@falladas_primera_vez==0){ print "no failed answers in the first round\n" } else{ $preguntar_solo_falladas="yes"; $repetir_menu="no" } } } return $answer; } ################################################## sub abortar{ #saving in the first round alters statistics, so it is disabled if ($primera_vez eq "no") { if ($escribir_flag eq "yes"){ &escribir; &escribir_repaso unless ($repaso eq "yes"); } } exit; } ################################################## sub asignar_idiomas{ if ($reverse eq "no"){ $idioma1=$espanol[$lista[$i]]; $idioma2=$aleman[$lista[$i]]; } else{ $idioma2=$espanol[$lista[$i]]; $idioma1=$aleman[$lista[$i]]; } } ################################################## sub corregir{ print "corregir \"$espanol[$lista[$i]]\": "; my $a=<>; chomp ($a); if ($a eq "") {$a = $espanol[$lista[$i]]} print "corregir \"$aleman[$lista[$i]]\": "; my $b=<>; chomp ($b); if ($b eq "") {$b = $aleman[$lista[$i]]} print "\"$a=$b\" correcto? (s/n): "; my $c=<>; chomp ($c); if ($c eq "s") { $espanol[$lista[$i]]=$a; $aleman[$lista[$i]]=$b; &escribir; print "corregido\!\n"; } } ################################################## sub leer_repaso{ my $file_repaso=$file . $repaso_extension; open(REPASO, $file_repaso) || die "Error de lectura: $file_repaso\n$!"; @elegidos = ; close(REPASO); foreach(@elegidos){ if ($veces[$_] !~ /^[1-9][0-9]*$/){ die "Incompatibility between files $file and $file_repaso\n" } } } ################################################## sub elegir{ srand; foreach(0..$tanda-1){ my $t=$probabilidades[0]; my $r=rand; my $i=0; while($t<$r){$t=$t+$probabilidades[$i+1]; $i++} push(@elegidos, splice(@preguntables, $i, 1)); my $p=splice(@probabilidades, $i, 1); foreach(@probabilidades){ $_=$_/(1-$p); #corregimos las probabilidades } } } ################################################## sub asignarprobabilidades{ my $maximo=1; my $numero_de_ceros=0; my $suma=0; foreach (@preguntables){ if ($veces[$_]==0){ push(@probabilidades, -1); $numero_de_ceros++; } else{ my $aux=&peso($veces[$_],$fallos[$_]); push(@probabilidades, $aux); if ($maximo<$aux) {$maximo=$aux} $suma=$suma+$aux; } } $suma=$suma+$numero_de_ceros*$maximo; foreach(@probabilidades){ $_=($_==-1)? $maximo/$suma: $_/$suma; } } ################################################## sub peso{ # Feel free to write your own function. # @_[0] is the number of times a word has been selected, # @_[1] is the number of failures, # $exponente is a global variable than can be used as a parameter. # The return should be the relative probability of each word: # for example, a word with return value of 3 # is 6 times more likely to be chosen # than a word with return value 0.5 ((1+@_[1])/@_[0])**$exponente #other example: 2**(1+@_[1]/@_[0])-1 } ################################################## sub activarflag{ if (@sv==0) {return } foreach(@sv){ if ($_ eq $seccionactual) {$flag=1; return} } $flag=0; } ################################################ sub subdivide{ my @four=split /[=\n]/; $espanol[$j]=$four[0]; $aleman[$j]=$four[1]; $veces[$j] = ($four[2] eq "")? 0 : $four[2]; $fallos[$j] = ($four[3] eq "")? 0 : $four[3]; } ####################################################### sub escribir{ foreach(@elegidos){ $lines[$_]=$espanol[$_]."=".$aleman[$_]."=". $veces[$_]."=".$fallos[$_]."\n"; } if (open(ESCRIBIR, ">$file")){ print ESCRIBIR @lines; close(ESCRIBIR); } else {print "Error de escritura: ", $file, "\n"; } } ##################################################### sub escribir_repaso{ my $file_repaso=$file . $repaso_extension; if (open(REPASO, ">$file_repaso")){ foreach(@elegidos){ print REPASO $_; print REPASO "\n"; } close(REPASO); } else {print "Error de escritura: ", $file_repaso, "\n"; } } ##################################################### sub parser{ my $fichero_flag="no"; my $seccion_flag="no"; my $exponente_flag="no"; foreach(@ARGV){ if ($_ =~ /^-([0-9]+$)/) {$tanda = $1} elsif ($_ eq "-h") {&ayuda } elsif ($_ eq "-ro") {$escribir_flag="no"} elsif ($_ eq "-l") {$listarsecciones="yes"} elsif ($_ eq "-r") {$reverse="yes"} elsif ($_ eq "-v") {$repaso="yes"} elsif ($_ eq "-s") {$seccion_flag="yes"} elsif ($_ eq "-e") {$exponente_flag="yes"} else { if ($seccion_flag eq "yes"){ push (@sv, split /,/); $seccion_flag="no"; } elsif ($exponente_flag eq "yes"){ $exponente = $_; $exponente_flag = "no"; die "Error: exponent must be a positive number\n" unless $exponente =~ /^\d*\.?\d+$/ ; } elsif ($fichero_flag eq "no"){ $file = $_; $fichero_flag="yes" } else {&usage; exit 1} } } $#ARGV=-1; } ##################################################### sub ayuda{ print "Tangobulario 0.7 Copyright (C) 2008-2009 Nancho Alvarez\n", "http://tomasluisdevictoria.org/nancho/tangobulario\n\n", "This program comes with ABSOLUTELY NO WARRANTY. ", "This is free software, and you are welcome to ", "redistribute it under certain conditions. ", "For details see .\n\n"; print "Tangobulario is a small program to assist you in learning vocabulary of foreign languages. ", "It uses a database file which contains ", "words and their translation. ", "It also mantains a statistic of the number of times a ", "word has been asked and failed.\n\n"; print "Each line of the database is a pair of words (and ", "optionally its associated statistics) separated by an '=' sign. ", "The file can contain also section names enclosed by square brackets.\n\n"; &usage; print "\nOptions:\n", "-# (# is a natural number)\n", "\t the number of words to use. Default is 25.\n", "-s section1,section2,...\n", "\t comma-separated list of sections (no spaces)\n", "-e exponent (exponent is a small non-negative real number)\n", "\t it affects the use of statistics. Default value is 1.5\n", "-l", "\t show a list of all sections contained in the database\n", "-r", "\t reverse the direction of the questions\n", "-v", "\t review mode: it asks the same words as in the previous run\n", "-ro", "\t read only: do not save statistics and review information\n", "-h", "\t this help\n\n"; print "Other commands (inside the program):\n", "=", "\t edition mode (disabled if -ro option is given)\n", "==", "\t accepts the answer as correct\n", "=exit", "\t exit the program saving statistics if applicable\n\n"; exit 0; } ##################################################### sub usage{ print "Usage: \n", "tangobulario [-#] [-h] [-l] [-ro] [-r] [-v] [-s section1,section2,...] [-e exponent] [database_file]\n"; }