Perl print()在循环迭代后添加换行符

时间:2017-05-12 17:02:34

标签: perl for-loop

我在Perl 5.24中制作Tic-Tac-Toe并正在努力以不同的颜色显示获胜的动作,所以我在棋盘中的每个角色中进行迭代并使用print()将字符显示在屏幕上。

我遇到的问题是,每次for循环递增时,print()都会为每个字符添加换行符。

我使用了thisthisthis,和 this并且还有换行符,所以在我看来问题是print()和/或循环如何在Perl中运行。

根据要求,这里是整个项目:

use 5.24.0;
use warnings;
use strict;

use Win32::Console;

my $CONSOLE = Win32::Console->new( STD_OUTPUT_HANDLE );

my $attr = $CONSOLE->Attr();

$| = 1;

use constant { TRUE => 1, FALSE => 0 };

use subs qw(pause);

my $player     = "X";
my @usedXMoves = [];
my @usedOMoves = [];

my $xScore = 0;
my $oScore = 0;

my $winningMove = "";

my %indsToCheck = (
    Top    => [ 0,  2,  4 ],
    Bottom => [ 12, 14, 16 ],
    Left   => [ 0,  6,  12 ],
    Right  => [ 4,  10, 16 ],
    MidV   => [ 2,  8,  14 ],
    MidH   => [ 6,  8,  10 ],
    DiaTB  => [ 0,  8,  16 ],
    DiaBT  => [ 12, 8,  4 ]
);

main();

sub main {
    my ( $gameBoard, %boardInds ) = ResetBoard();

    DisplayBoardAndInds( $gameBoard, \%boardInds );

    my $res = -1;
    while ( TRUE ) {
        if ( -2 == $res ) {
            DisplayScores( TRUE );

            last;
        }
        elsif ( 1 == $res ) {

            # Update score first
            if   ( "X" eq $player ) { $xScore++; }
            else                    { $oScore++; }

            say "\n\nWe have a winner!\nWould you like to play again? (Y/N) ";
            my $again = <STDIN>;
            chomp $again;

            if ( "Y" eq uc( $again ) ) {
                ( $gameBoard, %boardInds ) = ResetBoard();
                $res = -1;

                DisplayScores();

                SwitchPlayer();
            }
            elsif ( "N" eq uc( $again ) ) {
                DisplayScores( TRUE );

                last;
            }
        }
        else {
            $res = ValidateAndApplyMove( GetInput( \%boardInds ), \$gameBoard );
        }

        DisplayBoardAndInds( $gameBoard, \%boardInds );
    }
}

sub pause {
    <STDIN>;
}

sub SetColor {
    $CONSOLE->Attr( shift );
}

sub ResetColor {
    $CONSOLE->Attr( $attr );
}

# Will display text in color
# in: Color, text
sub PrintInColor {
    SetColor( shift );

    $CONSOLE->Write( shift );

    ResetColor();
}

# Resets the gameboard and playable indicies
sub ResetBoard {
    my $gameBoard = "_|_|_\n_|_|_\n | | ";
    my %boardInds = (
        TL => 0,
        TM => 2,
        TR => 4,
        ML => 6,
        MM => 8,
        MR => 10,
        BL => 12,
        BM => 14,
        BR => 16
    );

    @usedXMoves = [];
    @usedOMoves = [];

    return ( $gameBoard, %boardInds );
}

# Displays the current board and usable moves
sub DisplayBoardAndInds {
    system( "cls" );

    my $board   = shift;
    my $refInds = shift;

    if ( "" ne $winningMove ) {
        my @winningInds = @{ $indsToCheck{$winningMove} };

        my $indInd  = 0;
        my $currInd = $winningInds[0];

        # TODO:
        # Figure out why new lines are being appended here

        use Data::Dumper;

        $Data::Dumper::Useqq = 1;
        $CONSOLE->Write( Dumper $board);
        $CONSOLE->Write( Dumper $FG_RED);
        pause;

        for my $i ( 0 .. length( $board ) - 1 ) {

            if ( $i eq $currInd ) {
                PrintInColor( $FG_RED, substr( $board, $i, 1 ) );
                $indInd++;
                $currInd = $winningInds[$indInd];
            }
            else {
                $CONSOLE->Write( substr( $board, $i, 1 ) );

            }

            pause;
        }

        $CONSOLE->Write( "\n" );

        $winningMove = "";
    }
    else {
        say $board;
    }

    say "Current player = $player\n";

    # Sort by value (using reference)
    foreach my $k ( sort { $refInds->{$a} <=> $refInds->{$b} } keys( %$refInds ) ) {
        $CONSOLE->Write( "$k, " );
    }

    $CONSOLE->Write( "EXIT\n" );
}

sub DisplayScores {

    system( "cls" );

    my $final = shift;

    if   ( $final ) {
        say "Final scores:";
    }
    else            {
        say "Scores:";
    }

    say "X: $xScore";
    say "O: $oScore";

    if ( $final ) {
        say "\n\nThanks for playing!";
    }

    pause;
}

# Retrieves and validates input
sub GetInput {

    $CONSOLE->Write( "What move would you like to make? " );
    my $move = <STDIN>;
    chomp $move;    # <STDIN> returns with a newline
    $move = uc( $move );

    my $refInds = shift;

    my $ret = -1;

    if ( "exit" eq lc( $move ) ) {
        $ret = -2;
    }
    if ( exists $refInds->{$move} ) {
        $ret = $refInds->{$move};
        delete $refInds->{$move};
    }

    return $ret;
}

# Displays any error message
# If none, then applies the move to the board
sub ValidateAndApplyMove {

    my $move      = shift;
    my $gameBoard = shift;

    if ( -2 == $move ) {
        return -2;
    }
    elsif ( -1 == $move ) {
        say "\n\nInvalid move. Please try again.";
        pause;
    }
    else {
        my $tBoard = ${$gameBoard};
        my $len    = length( $tBoard );

        $tBoard = substr( $tBoard, 0, $move ) . $player . substr( $tBoard, $move + 1, $len );

        ${$gameBoard} = $tBoard;

        if ( "X" eq $player ) {
            push @usedXMoves, $move;
        }
        else {
            push @usedOMoves, $move;
        }

        if ( CheckWin() ) {
            return 1;
        }

        #SwitchPlayer();
    }

    return 0;
}

# Updates the current player
sub SwitchPlayer {
    if ( "X" eq $player ) {
        $player = "O";
    }
    else {
        $player = "X";
    }
}

# Checks current moves against winning conditions
sub CheckWin {

    my @moves = [];

    if ( "X" eq $player ) {
        @moves = @usedXMoves;
    }
    else {
        @moves = @usedOMoves;
    }

    return (
           GetRow( "Top", @moves )
        or GetRow( "MidH",   @moves )
        or GetRow( "Bottom", @moves )
        or GetRow( "Left",   @moves )
        or GetRow( "MidV",   @moves )
        or GetRow( "Right",  @moves )
        or GetRow( "DiaTB",  @moves )
        or GetRow( "DiaBT",  @moves )
    );
}

# Returns true if the passed in array contains all the values for a given row

sub GetRow {
    my $row   = shift;
    my @moves = @_;

    # Convert array to map
    my %moves = map { $_ => 1 } @moves;

    my @indsToCheck = @{ $indsToCheck{$row} };

    foreach ( @indsToCheck ) {
        if ( !exists( $moves{$_} ) ) {
            return FALSE;
        }
    }

    $winningMove = $row;

    return TRUE;
}

董事会成员:

X|X|X
O|X|_
O|_|O

输出:

X  
|  
X  
|  
X  
...

那么如何在循环中不使用换行符进行打印?

2 个答案:

答案 0 :(得分:3)

更新

看过你的完整代码,我确切地知道发生了什么

你的程序有这个循环

for my $i ( 0 .. length( $board ) - 1 ) {

    if ( $i eq $currInd ) {
        PrintInColor( $FG_RED, substr( $board, $i, 1 ) );
        $indInd++;
        $currInd = $winningInds[$indInd];
    }
    else {
        $CONSOLE->Write( substr( $board, $i, 1 ) );
    }

    pause;
}

其中pause

sub pause {
    <STDIN>;
}

这意味着你必须按返回来逐步完成循环。每次执行此操作时,都会在屏幕上显示换行符。删除pause,它将起作用


除非您自己在其他地方添加了代码,否则您展示的代码没有理由打印换行符

我能为您做的最好的事情是发布一个示例程序,该程序使用您在$board中所说的字符串,并以 X 字符显示为红色。请注意,Attr()不会重置颜色;您必须使用Attr($FG_WHITE)

将其设置为白色(或其他)
use strict;
use warnings 'all';

use Win32::Console;

my $c = Win32::Console->new(STD_OUTPUT_HANDLE);

$c->Cls($FG_WHITE | $BG_BLACK);

my $board = "X|X|X\n_|_|_\n | | | ";

for my $ch ( split //, $board ) {

    if ( $ch eq 'X' ) {
        $c->Attr($FG_LIGHTRED);
        $c->Write($ch);
        $c->Attr($FG_WHITE);
    }
    else {
        $c->Write($ch);
    }
}

答案 1 :(得分:1)

如果$board不包含任何换行符,那么您声称print正在为您要打印的内容添加换行符。默认情况下,print不会附加任何内容。如果它附加换行符,则必须设置$\ = "\n";(可能使用-l)。