在基于网格的游戏中确定可能 moves/path
Determining possible moves/path in grid based game
背景:
我正在开发一款将 3 个圆圈放到网格上并允许用户通过点击圆圈而不是在网格上点击另一个 space 来一次移动一个圆圈的游戏。每一步都会在网格上掉落更多的圆圈。
虽然游戏的运作方式是,如果没有通往所选地点的明确路径(例如,您被其他圈子挡住了,或者您没有被挡住,但棋盘目前没有路径打开 spaces 到那个点)游戏会提醒你你试图做出无效的移动并将拒绝该移动。
我的问题:
我想不出一种方法来获取代码来检查所播放(点击)的动作是有效还是无效。
我给每个按钮一个标签,上面有一个对应于它在网格上的位置的数字(见代码.m)
例子:
在游戏的这一点上,从 space #70 -> #39 的移动是无效的,而从 #70 -> #71 的移动是可以的。
-注意:对角线移动也是无效的。
我怎样才能获得确定这一点的代码?
代码:(.m)
#import "ViewController.h"
@interface ViewController ()
@end
int name;
bool oneSelect;
UIImage *imageName;
UIButton *selectedButton;
NSArray *items;
NSMutableArray *occupied;
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
items = [NSArray arrayWithObjects:@"marble_red",@"marble_blue",@"marble_yellow",@"marble_green",@"marble_purple",@"marble_black",@"marble_orange",nil];
for (int y=0; y < 9; y++) {
for (int x = 0; x < 9; x++) {
UIButton * button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(22 + 30 * x, 100 + 30 * y, 30, 30);
unsigned buttonNumber = name;
button.tag = buttonNumber;
[button setTitle:[NSString stringWithFormat:@"%u", buttonNumber] forState:UIControlStateNormal];
[button addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview: button];
name= name +1;
}
}
for (int i = 0; i <= 2; i++) {
int space = arc4random()%81;
int pic = arc4random()%7;
NSString *string = [items objectAtIndex:pic];
UIButton *button = (UIButton *)[self.view viewWithTag:space];
[button setBackgroundImage:[UIImage imageNamed:string] forState:UIControlStateNormal];
NSLog(@"String(%@) / Button(%@)",string,button.description);
}
}
-(void)buttonPressed:(UIButton *)button
{
if (!oneSelect) {
if ([button backgroundImageForState:UIControlStateNormal] != NULL) {
button.selected = YES;
imageName = [button backgroundImageForState:UIControlStateNormal];
selectedButton = button;
oneSelect = YES;
}else{
button.selected = NO;
}
}else{
if ([button backgroundImageForState:UIControlStateNormal] == NULL) {
oneSelect = NO;
[button setBackgroundImage:imageName forState:UIControlStateNormal];
button.selected = NO;
[selectedButton setBackgroundImage:[UIImage imageNamed:@""] forState:UIControlStateNormal];
selectedButton.selected = NO;
[self nextMove];
}else{
selectedButton.selected = NO;
button.selected = NO;
}
}
NSLog(@"button %ld -- frame: %@", (long)button.tag, NSStringFromCGRect(button.frame));
}
-(void)nextMove{
for (int i = 0; i <= 2; i++) {
int space = arc4random()%81;
int pic = arc4random()%7;
NSString *string = [items objectAtIndex:pic];
UIButton *button = (UIButton *)[self.view viewWithTag:space];
if ([button backgroundImageForState:UIControlStateNormal] == NULL) {
[button setBackgroundImage:[UIImage imageNamed:string] forState:UIControlStateNormal];
}else{
i = i - 1;
}
NSLog(@"String(%@) / Button(%@)",string,button.description);
}
}
此外,如果我应该在我的代码中修复任何可以改善游戏玩法的内容,请告诉我。
基本上,您想查看当前位置是否与所选位置相距一格。
有几个步骤可以验证这一点。首先,找到两个位置的difference
。如果两个点的绝对差等于每行的长度(在本例中为 9),则表示所选位置高于或低于您当前的位置,从而使移动有效。
如果该步骤失败,则需要检查这两个点是否相邻,如果相邻,请确保它们在同一行。为此,您需要查看 difference
是否等于 1,如果等于,则将每个位置除以行的长度并查看它们是否相等。你剩下的是类似于这样的东西:
int difference = Absolute(CURRENT - SELECTED)
int CURRENT_ROW_NUMBER = CURRENT / ROW_LENGTH
int SELECTED_ROW_NUMBER = SELECTED / ROW_LENGTH
if(difference == ROW_LENGTH)
{
ValidMove();
}
else if(difference == 1 && CURRENT_ROW_NUMBER == SELECTED_ROW_NUMBER))
{
ValidMove();
}
else
{
InvalidMove();
}
我不太熟悉这门语言,但这就是您所尝试的背后的逻辑。
祝你游戏顺利!
我的方法是实施 Iterative Deepening Depth First Search
首先,我创建了一个 class 来表示面板中的每个 'cell'(因为我不喜欢使用 viewWithTag
BoardCell.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface BoardCell : NSObject
@property (weak,nonatomic,readonly) UIButton *button;
@property BOOL occupied;
-(id) initWithButton:(UIButton *)button;
@end
BoardCell.m
#import "BoardCell.h"
@implementation BoardCell
-(id) initWithButton:(UIButton *)button {
if (self=[super init]) {
_button=button;
}
return self;
}
@end
然后我实现了我的游戏版本(我更改了图像以使用我已有的图像,所以我也更改了单元格布局的尺寸)。我不太确定你的游戏 'rules',所以我编了一些。它不检查 "game over" 但这是微不足道的。
ViewController.m
#import "ViewController.h"
#import "BoardCell.h"
@interface ViewController ()
@property (strong,nonatomic) NSArray *imageNames;
@property (strong,nonatomic) NSMutableArray *board;
@property NSInteger lastMove;
#define BOARDWIDTH 9
#define BOARDHEIGHT 9
@end
static int moves[]={-BOARDWIDTH,-1,1,BOARDWIDTH};
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.imageNames = @[@"redhex",@"bluehex",@"purplehex",@"orangehex",@"greenhex"];
self.board = [NSMutableArray new];
for (int y=0; y < BOARDWIDTH; y++) {
for (int x = 0; x < BOARDHEIGHT; x++) {
UIButton * button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(40 * x, 100 + 40 * y, 60, 60);
button.tag = y*BOARDWIDTH+x;
[button setTitle:[NSString stringWithFormat:@"%ld", button.tag] forState:UIControlStateNormal];
[button addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview: button];
[self.board addObject:[[BoardCell alloc] initWithButton:button]];
}
}
self.lastMove=arc4random_uniform(BOARDWIDTH*BOARDHEIGHT);
UIButton *button=(UIButton *)[self.view viewWithTag:self.lastMove];
[button setBackgroundImage:[UIImage imageNamed:@"greensquare"] forState:UIControlStateNormal];
[self addRandoms:3];
}
-(void) addRandoms:(NSInteger)randomCount {
for (int i = 0; i < randomCount; i++) {
int space = arc4random_uniform(BOARDWIDTH*BOARDHEIGHT);
BoardCell *cell=self.board[space];
if (!cell.occupied) {
int pic = arc4random_uniform((u_int32_t)self.imageNames.count);
NSString *string = [self.imageNames objectAtIndex:pic];
[cell.button setBackgroundImage:[UIImage imageNamed:string] forState:UIControlStateNormal];
cell.occupied=YES;
}
else {
i--;
}
}
}
-(void)buttonPressed:(UIButton *)button
{
NSInteger buttonId=button.tag;
BoardCell *cell=self.board[buttonId];
if (!cell.occupied) {
BoardCell *startCell=self.board[self.lastMove];
startCell.occupied=NO;
if ([self validMoveFromSquare:self.lastMove toDestination:buttonId]) {
[startCell.button setBackgroundImage:[UIImage imageNamed:@"orangesquare"] forState:UIControlStateNormal];
[cell.button setBackgroundImage:[UIImage imageNamed:@"greensquare"] forState:UIControlStateNormal];
self.lastMove=buttonId;
cell.occupied=YES;
}
startCell.occupied=YES;
}
[self addRandoms:3];
}
-(BOOL) validMoveFromSquare:(NSInteger)startSquare toDestination:(NSInteger)destination {
for (int limit=1;limit<50;limit++ ) {
NSMutableIndexSet *visitList=[NSMutableIndexSet new];
if ([self DFSFromStart:startSquare toGoal:destination withLimit:limit andVisitList:visitList]) {
return YES;
}
}
return NO;
}
-(BOOL) DFSFromStart:(NSInteger)start toGoal:(NSInteger)goal withLimit:(NSInteger)limit andVisitList:(NSMutableIndexSet *)visitList {
if (start==goal) {
return YES;
}
if (limit >=0) {
if (((BoardCell *)self.board[start]).occupied) {
return NO;
}
[visitList addIndex:start];
for (int i=0;i<4;i++) {
NSInteger nextPosition=start+moves[i];
if ([self validDestination:nextPosition withMove:moves[i] fromSquare:start]) {
if (![visitList containsIndex:nextPosition]) {
if ([self DFSFromStart:nextPosition toGoal:goal withLimit:limit-1 andVisitList:visitList]) {
return YES;
}
}
}
}
}
return NO;
}
-(BOOL) validDestination:(NSInteger)destination withMove:(int)move fromSquare:(NSInteger)start {
if (destination <0 || destination >= BOARDWIDTH*BOARDHEIGHT) {
return NO;
}
if (abs(move)==1) {
int sourceRow= (int)start / BOARDWIDTH;
int destinationRow=(int)destination / BOARDWIDTH;
if (sourceRow != destinationRow) {
return NO;
}
}
return YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
编辑 我最初误读了你的问题,所以我的代码允许对角线移动。我已经修复了这个
编辑 2 我已将 DFS 限制增加到 50 并添加了一种新方法来正确检查有效移动 - 你不能移动 'left' 或 'right' 到不同的行。 - 查看最后的新方法以及在 DFSFromStart
中对该方法的调用
编辑 3 DFSFromStart 在比较是否达到目标之前没有考虑移动的有效性。
背景: 我正在开发一款将 3 个圆圈放到网格上并允许用户通过点击圆圈而不是在网格上点击另一个 space 来一次移动一个圆圈的游戏。每一步都会在网格上掉落更多的圆圈。
虽然游戏的运作方式是,如果没有通往所选地点的明确路径(例如,您被其他圈子挡住了,或者您没有被挡住,但棋盘目前没有路径打开 spaces 到那个点)游戏会提醒你你试图做出无效的移动并将拒绝该移动。
我的问题: 我想不出一种方法来获取代码来检查所播放(点击)的动作是有效还是无效。
我给每个按钮一个标签,上面有一个对应于它在网格上的位置的数字(见代码.m)
例子:
-注意:对角线移动也是无效的。
我怎样才能获得确定这一点的代码?
代码:(.m)
#import "ViewController.h"
@interface ViewController ()
@end
int name;
bool oneSelect;
UIImage *imageName;
UIButton *selectedButton;
NSArray *items;
NSMutableArray *occupied;
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
items = [NSArray arrayWithObjects:@"marble_red",@"marble_blue",@"marble_yellow",@"marble_green",@"marble_purple",@"marble_black",@"marble_orange",nil];
for (int y=0; y < 9; y++) {
for (int x = 0; x < 9; x++) {
UIButton * button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(22 + 30 * x, 100 + 30 * y, 30, 30);
unsigned buttonNumber = name;
button.tag = buttonNumber;
[button setTitle:[NSString stringWithFormat:@"%u", buttonNumber] forState:UIControlStateNormal];
[button addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview: button];
name= name +1;
}
}
for (int i = 0; i <= 2; i++) {
int space = arc4random()%81;
int pic = arc4random()%7;
NSString *string = [items objectAtIndex:pic];
UIButton *button = (UIButton *)[self.view viewWithTag:space];
[button setBackgroundImage:[UIImage imageNamed:string] forState:UIControlStateNormal];
NSLog(@"String(%@) / Button(%@)",string,button.description);
}
}
-(void)buttonPressed:(UIButton *)button
{
if (!oneSelect) {
if ([button backgroundImageForState:UIControlStateNormal] != NULL) {
button.selected = YES;
imageName = [button backgroundImageForState:UIControlStateNormal];
selectedButton = button;
oneSelect = YES;
}else{
button.selected = NO;
}
}else{
if ([button backgroundImageForState:UIControlStateNormal] == NULL) {
oneSelect = NO;
[button setBackgroundImage:imageName forState:UIControlStateNormal];
button.selected = NO;
[selectedButton setBackgroundImage:[UIImage imageNamed:@""] forState:UIControlStateNormal];
selectedButton.selected = NO;
[self nextMove];
}else{
selectedButton.selected = NO;
button.selected = NO;
}
}
NSLog(@"button %ld -- frame: %@", (long)button.tag, NSStringFromCGRect(button.frame));
}
-(void)nextMove{
for (int i = 0; i <= 2; i++) {
int space = arc4random()%81;
int pic = arc4random()%7;
NSString *string = [items objectAtIndex:pic];
UIButton *button = (UIButton *)[self.view viewWithTag:space];
if ([button backgroundImageForState:UIControlStateNormal] == NULL) {
[button setBackgroundImage:[UIImage imageNamed:string] forState:UIControlStateNormal];
}else{
i = i - 1;
}
NSLog(@"String(%@) / Button(%@)",string,button.description);
}
}
此外,如果我应该在我的代码中修复任何可以改善游戏玩法的内容,请告诉我。
基本上,您想查看当前位置是否与所选位置相距一格。
有几个步骤可以验证这一点。首先,找到两个位置的difference
。如果两个点的绝对差等于每行的长度(在本例中为 9),则表示所选位置高于或低于您当前的位置,从而使移动有效。
如果该步骤失败,则需要检查这两个点是否相邻,如果相邻,请确保它们在同一行。为此,您需要查看 difference
是否等于 1,如果等于,则将每个位置除以行的长度并查看它们是否相等。你剩下的是类似于这样的东西:
int difference = Absolute(CURRENT - SELECTED)
int CURRENT_ROW_NUMBER = CURRENT / ROW_LENGTH
int SELECTED_ROW_NUMBER = SELECTED / ROW_LENGTH
if(difference == ROW_LENGTH)
{
ValidMove();
}
else if(difference == 1 && CURRENT_ROW_NUMBER == SELECTED_ROW_NUMBER))
{
ValidMove();
}
else
{
InvalidMove();
}
我不太熟悉这门语言,但这就是您所尝试的背后的逻辑。
祝你游戏顺利!
我的方法是实施 Iterative Deepening Depth First Search
首先,我创建了一个 class 来表示面板中的每个 'cell'(因为我不喜欢使用 viewWithTag
BoardCell.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface BoardCell : NSObject
@property (weak,nonatomic,readonly) UIButton *button;
@property BOOL occupied;
-(id) initWithButton:(UIButton *)button;
@end
BoardCell.m
#import "BoardCell.h"
@implementation BoardCell
-(id) initWithButton:(UIButton *)button {
if (self=[super init]) {
_button=button;
}
return self;
}
@end
然后我实现了我的游戏版本(我更改了图像以使用我已有的图像,所以我也更改了单元格布局的尺寸)。我不太确定你的游戏 'rules',所以我编了一些。它不检查 "game over" 但这是微不足道的。
ViewController.m
#import "ViewController.h"
#import "BoardCell.h"
@interface ViewController ()
@property (strong,nonatomic) NSArray *imageNames;
@property (strong,nonatomic) NSMutableArray *board;
@property NSInteger lastMove;
#define BOARDWIDTH 9
#define BOARDHEIGHT 9
@end
static int moves[]={-BOARDWIDTH,-1,1,BOARDWIDTH};
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.imageNames = @[@"redhex",@"bluehex",@"purplehex",@"orangehex",@"greenhex"];
self.board = [NSMutableArray new];
for (int y=0; y < BOARDWIDTH; y++) {
for (int x = 0; x < BOARDHEIGHT; x++) {
UIButton * button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(40 * x, 100 + 40 * y, 60, 60);
button.tag = y*BOARDWIDTH+x;
[button setTitle:[NSString stringWithFormat:@"%ld", button.tag] forState:UIControlStateNormal];
[button addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview: button];
[self.board addObject:[[BoardCell alloc] initWithButton:button]];
}
}
self.lastMove=arc4random_uniform(BOARDWIDTH*BOARDHEIGHT);
UIButton *button=(UIButton *)[self.view viewWithTag:self.lastMove];
[button setBackgroundImage:[UIImage imageNamed:@"greensquare"] forState:UIControlStateNormal];
[self addRandoms:3];
}
-(void) addRandoms:(NSInteger)randomCount {
for (int i = 0; i < randomCount; i++) {
int space = arc4random_uniform(BOARDWIDTH*BOARDHEIGHT);
BoardCell *cell=self.board[space];
if (!cell.occupied) {
int pic = arc4random_uniform((u_int32_t)self.imageNames.count);
NSString *string = [self.imageNames objectAtIndex:pic];
[cell.button setBackgroundImage:[UIImage imageNamed:string] forState:UIControlStateNormal];
cell.occupied=YES;
}
else {
i--;
}
}
}
-(void)buttonPressed:(UIButton *)button
{
NSInteger buttonId=button.tag;
BoardCell *cell=self.board[buttonId];
if (!cell.occupied) {
BoardCell *startCell=self.board[self.lastMove];
startCell.occupied=NO;
if ([self validMoveFromSquare:self.lastMove toDestination:buttonId]) {
[startCell.button setBackgroundImage:[UIImage imageNamed:@"orangesquare"] forState:UIControlStateNormal];
[cell.button setBackgroundImage:[UIImage imageNamed:@"greensquare"] forState:UIControlStateNormal];
self.lastMove=buttonId;
cell.occupied=YES;
}
startCell.occupied=YES;
}
[self addRandoms:3];
}
-(BOOL) validMoveFromSquare:(NSInteger)startSquare toDestination:(NSInteger)destination {
for (int limit=1;limit<50;limit++ ) {
NSMutableIndexSet *visitList=[NSMutableIndexSet new];
if ([self DFSFromStart:startSquare toGoal:destination withLimit:limit andVisitList:visitList]) {
return YES;
}
}
return NO;
}
-(BOOL) DFSFromStart:(NSInteger)start toGoal:(NSInteger)goal withLimit:(NSInteger)limit andVisitList:(NSMutableIndexSet *)visitList {
if (start==goal) {
return YES;
}
if (limit >=0) {
if (((BoardCell *)self.board[start]).occupied) {
return NO;
}
[visitList addIndex:start];
for (int i=0;i<4;i++) {
NSInteger nextPosition=start+moves[i];
if ([self validDestination:nextPosition withMove:moves[i] fromSquare:start]) {
if (![visitList containsIndex:nextPosition]) {
if ([self DFSFromStart:nextPosition toGoal:goal withLimit:limit-1 andVisitList:visitList]) {
return YES;
}
}
}
}
}
return NO;
}
-(BOOL) validDestination:(NSInteger)destination withMove:(int)move fromSquare:(NSInteger)start {
if (destination <0 || destination >= BOARDWIDTH*BOARDHEIGHT) {
return NO;
}
if (abs(move)==1) {
int sourceRow= (int)start / BOARDWIDTH;
int destinationRow=(int)destination / BOARDWIDTH;
if (sourceRow != destinationRow) {
return NO;
}
}
return YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
编辑 我最初误读了你的问题,所以我的代码允许对角线移动。我已经修复了这个
编辑 2 我已将 DFS 限制增加到 50 并添加了一种新方法来正确检查有效移动 - 你不能移动 'left' 或 'right' 到不同的行。 - 查看最后的新方法以及在 DFSFromStart
编辑 3 DFSFromStart 在比较是否达到目标之前没有考虑移动的有效性。