visual_memory_test.pl 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 22 September 2019
  4. # https://github.com/trizen
  5. # A simple program that can solve the "Visual Memory Test" from Human Benchmark.
  6. # https://www.humanbenchmark.com/tests/memory
  7. # The program uses the `maim` and `swarp` tools to control the mouse.
  8. # See also:
  9. # https://github.com/naelstrof/maim
  10. # https://tools.suckless.org/x/swarp/
  11. # The current highest level reached by this program is 38.
  12. use 5.020;
  13. use strict;
  14. use warnings;
  15. use GD qw();
  16. use Time::HiRes qw(sleep);
  17. use experimental qw(signatures);
  18. GD::Image->trueColor(1);
  19. sub avg {
  20. ($_[0] + $_[1] + $_[2]) / 3;
  21. }
  22. sub img2ascii ($image) {
  23. my $size = 1920;
  24. my $img = GD::Image->new($image) // return;
  25. my ($width, $height) = $img->getBounds;
  26. if ($size != 0) {
  27. my $scale_width = $size;
  28. my $scale_height = int($height / ($width / ($size / 2)));
  29. my $resized = GD::Image->new($scale_width, $scale_height);
  30. $resized->copyResampled($img, 0, 0, 0, 0, $scale_width, $scale_height, $width, $height);
  31. ($width, $height) = ($scale_width, $scale_height);
  32. $img = $resized;
  33. }
  34. my $avg = 0;
  35. my @averages;
  36. foreach my $y (0 .. $height - 1) {
  37. foreach my $x (0 .. $width - 1) {
  38. my $index = $img->getPixel($x, $y);
  39. push @averages, avg($img->rgb($index));
  40. $avg += $averages[-1] / $width / $height;
  41. }
  42. }
  43. unpack("(A$width)*", join('', map { $_ < $avg ? 1 : 0 } @averages));
  44. }
  45. sub solve (@lines) {
  46. my $width_offset = 760;
  47. my $height_offset = 130;
  48. @lines = @lines[$height_offset - 1 .. 320];
  49. while (@lines and $lines[0] =~ /^1+\z/) {
  50. shift @lines;
  51. ++$height_offset;
  52. }
  53. @lines = map { substr($_, $width_offset, 385) } @lines;
  54. my $square_height = 0;
  55. foreach my $i (0 .. $#lines) {
  56. if ($lines[$i] =~ /0/) {
  57. ++$square_height;
  58. }
  59. if ($square_height > 0 and $lines[$i] !~ /0/) {
  60. last;
  61. }
  62. }
  63. if ($square_height == 0) {
  64. warn "Can't determine square height...";
  65. return;
  66. }
  67. my $left_index = 0;
  68. my $square_width = 0;
  69. OUTER: foreach my $i (0 .. 100) {
  70. foreach my $line (@lines) {
  71. if (substr($line, $i, 1) eq '0') {
  72. $left_index = $i;
  73. $line =~ /^1*(0+)/;
  74. $square_width = length($1);
  75. last OUTER;
  76. }
  77. }
  78. }
  79. if ($square_width == 0) {
  80. warn "Can't determine square width...";
  81. return;
  82. }
  83. say "Left index: $left_index";
  84. say "Square width: $square_width";
  85. say "Square height: $square_height";
  86. my @grid;
  87. my $size = int(length($lines[0]) / $square_width);
  88. if ($size < 3) {
  89. warn "Can't determine the size of the grid...";
  90. return;
  91. }
  92. if ($size > 20) {
  93. warn "Incorrect size of the grid...";
  94. return;
  95. }
  96. my $width_gap = 10;
  97. my $height_gap = 4;
  98. if ($size >= 6) {
  99. $width_gap = 9;
  100. $height_gap = 3;
  101. }
  102. if ($size >= 8) {
  103. $width_gap = 8;
  104. }
  105. if ($size >= 10) {
  106. $width_gap = 5;
  107. }
  108. if ($size >= 11) {
  109. $width_gap = 4;
  110. $height_gap = 2;
  111. }
  112. say "Size: $size x $size";
  113. foreach my $i (0 .. $size - 1) {
  114. foreach my $j (0 .. $size - 1) {
  115. my @square;
  116. foreach my $line (
  117. @lines[$square_height * $i + $height_gap * $i .. $square_height * $i + $height_gap * $i + $square_height - 1])
  118. {
  119. push @square, substr($line, $square_width * $j + $width_gap * $j, $square_width);
  120. }
  121. $grid[$i][$j] = \@square;
  122. }
  123. }
  124. my @matrix;
  125. foreach my $i (0 .. $#grid) {
  126. my $row = $grid[$i];
  127. foreach my $j (0 .. $#$row) {
  128. my $square = $row->[$j];
  129. my %freq = ('0' => 0, '1' => 0);
  130. ++$freq{$_} for split(//, join('', @$square));
  131. $matrix[$i][$j] = ($freq{'0'} > $freq{'1'}) ? 1 : 0;
  132. }
  133. }
  134. say "@$_" for @matrix;
  135. foreach my $i (0 .. $#matrix) {
  136. foreach my $j (0 .. $#{$matrix[0]}) {
  137. if ($matrix[$i][$j]) {
  138. my $x = int($width_offset + $square_width * $j + $square_width / 2 + $width_gap * $j);
  139. my $y = int(2 * $height_offset + $square_height * 2 * $i + $square_height / 2 + 2 * $height_gap * $i);
  140. #say "Changing pointer to ($x, $y)";
  141. system("swarp", $x, $y);
  142. #say "Clicking square...";
  143. system("xdotool", "click", "1");
  144. }
  145. }
  146. }
  147. }
  148. if (@ARGV) {
  149. solve(img2ascii($ARGV[0]));
  150. exit;
  151. }
  152. while (1) {
  153. print "Press <ENTER> to take screenshot: ";
  154. my $prompt = <STDIN>;
  155. my $sshot = `maim --geometry '1920x700+0+0' --format=jpg /dev/stdout`;
  156. my @lines = img2ascii($sshot);
  157. sleep 1;
  158. solve(@lines);
  159. system("swarp", 1700, 800);
  160. system("xdotool", "click", "1");
  161. }