## Posts Tagged ‘魔方’

### 解魔方的机器人攻略28 – 总结

NXT端的程序下载(源代码)

PC端的程序下载(exe)

http://singularityhub.com/2010/02/17/lego-robot-solves-any-rubiks-cube-in-less-than-12-seconds-video/

http://blog.izxg.com/?tag=%E4%B9%90%E9%AB%98

### 解魔方的机器人攻略25 – 解魔方

1，蓝牙连接成功
2，NXT扫描魔方，发送6个面，每个面9块共54组颜色数据到电脑
3，NXT发送一个字节（0xFF）到电脑，表示颜色读取完毕
4，电脑开始计算解法，得到解魔方的步骤，一共N步
5，电脑发送一个字节N到NXT
6，NXT进行从1到N的循环，每次发送一个字节n到电脑，请求第n步操作
7，电脑发送第n步操作给NXT
8，NXT执行完全部N个操作，发送一个字节(0xFE)到电脑，通知解魔方完成
9，电脑清空步骤和颜色数组，准备迎接下一次任务
10，按下Escape按钮，NXT发送三个(0XFF)给电脑，关闭蓝牙连接并退出

```public class CubeCenter
{
public string[] CenterColor = new string[6] { "U", "R", "D", "L", "F", "B" };

public void RotateBottom(bool colockwise)
{
if (colockwise)
{
string n = CenterColor[5];
CenterColor[5] = CenterColor[1];
CenterColor[1] = CenterColor[4];
CenterColor[4] = CenterColor[3];
CenterColor[3] = n;
}
else
{
string n = CenterColor[5];
CenterColor[5] = CenterColor[3];
CenterColor[3] = CenterColor[4];
CenterColor[4] = CenterColor[1];
CenterColor[1] = n;
}
}

public void RotatePaw()
{
//Only can move forward
string n = CenterColor[0];
CenterColor[0] = CenterColor[3];
CenterColor[3] = CenterColor[2];
CenterColor[2] = CenterColor[1];
CenterColor[1] = n;
}

public int FindCenter(string position)
{
int center = -1;
for (int i = 0; i < 6; i++)
{
if (CenterColor[i] == position) center = i;
}
return center;
}
}
```

P: Paw 爪子翻动一次
B：RotateBottom 从底面旋转魔方，后面需要接一个1~3的数字
S：RotateBottomSide 旋转魔方的底面，跟B的区别是这时候爪子抓住上两层，然后旋转底面

```int findSidePosition = CenterStatus.FindCenter(targetSide);

//Rotate to corrent bottom
switch (findSidePosition)
{
case 2:
//Do Nothing
break;
case 1:
CenterStatus.RotatePaw();
break;
case 0:
CenterStatus.RotatePaw();
CenterStatus.RotatePaw();
break;
case 3:
CenterStatus.RotateBottom(true);
CenterStatus.RotateBottom(true);
CenterStatus.RotatePaw();
break;
case 4:
CenterStatus.RotateBottom(true);
CenterStatus.RotatePaw();
break;
case 5:
CenterStatus.RotateBottom(false);
CenterStatus.RotatePaw();
break;
}
Steps[Steps.Count - 1].OrginStep = currentStep;
```

P B3 P S2 B1 P S1

```switch (MoveType)
{
case MoveType.RotatePaw:
return (byte)10;
case MoveType.RotateBottom:
return (byte)(20 + Count);
case MoveType.RotateBottomSide:
return (byte)(30 + Count);
default:
return (byte)0;
}
```

```//Get result
if(step==10)
{
//Rotate paw
Robot.RotatePaw();
}
else if(step>=20 && step<30)
{
//Rotate Bottom
int count = step - 20;
if(count == 3) count = -1;
Robot.RotateBottom(count);
}
else if(step>=30 && step<40)
{
//Rotate Bottom Side
int count = step - 30;
if(count == 3) count = -1;
Robot.RotateBottomSide(count);
}
```

1，用超声波测距传感器（就是那对眼睛）制作“开关”；
2，读色错误，卡住等情况的异常处理
3，语音提示，让萝卜头开口说话
4，暂停功能，帮助我们进行调试

### 解魔方的机器人攻略24 – 识别颜色（下）

1，攻略里已经讲过的。例如：请问解魔方的算法是什么？我很久以前就发过代码了（不过估计这样的同学也看不到这个声明，惆怅啊）。
3，对于参加竞赛的，做毕设的，或者保研需要加分的。非常抱歉，时间紧是您自己的事。我不会帮助投机取巧的行为，况且我其实比你们更忙。

4，设置app.config

```<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
</appSettings>
</configuration>
```

5，定义ColorItem类和排序类

```public class ColorItem
{
public int R, G, B, RawR, RawG, RawB;
public int Max, Min, RawMax, RawMin;
public int I, J, K;
//省略一些赋值操作
}
```

```public class ColorItemCompare : IComparer
{
private string CompareExpression;

public ColorItemCompare() { }
public ColorItemCompare(string exp)
{
CompareExpression = exp;
}

public int Compare(ColorItem c1, ColorItem c2)
{
if (c1 == null || c2 == null) return 0;
else
{
return GetEvalOfColor(c1, CompareExpression) - GetEvalOfColor(c2, CompareExpression);
}
}

private int GetEvalOfColor(ColorItem c, string exp)
{
string realExp = exp.ToLower();
realExp = realExp.Replace("rawmin", c.RawMin.ToString());
realExp = realExp.Replace("rawmax", c.RawMax.ToString());
realExp = realExp.Replace("min", c.Min.ToString());
realExp = realExp.Replace("max", c.Max.ToString());
realExp = realExp.Replace("rawr", c.RawR.ToString());
realExp = realExp.Replace("rawg", c.RawG.ToString());
realExp = realExp.Replace("rawb", c.RawB.ToString());
realExp = realExp.Replace("r", c.R.ToString());
realExp = realExp.Replace("g", c.G.ToString());
realExp = realExp.Replace("b", c.B.ToString());
return Convert.ToInt32(Evaluator.Eval(realExp));
}
}
```

```for (int n = 0; n < 6; n++)
{
string[] rankStr = ConfigurationSettings.AppSettings["Rank" + n].Split(':');
string resultColor = rankStr[0];
string compareExp = rankStr[1];

ColorItems.Sort(new ColorItemCompare(compareExp));
for (int i = 0; i < 9; i++)
{
ColorItem item = ColorItems[ColorItems.Count - 1];
int ijk = item.I * 100 + item.J * 10 + item.K;
ColorItems.RemoveAt(ColorItems.Count - 1);
}
}
```

6，生成魔方数组

```private string[] ReadColors()
{
string ColorStr = "";
string RealStr = "";
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 3; j++)
{
for (int k = 0; k < 3; k++)
{
if (!string.IsNullOrEmpty(ColorStr))
{
ColorStr += ",";
RealStr += ",";
}
int c = i * 100 + j * 10 + k;
string r = ColorSortResult[c];
ColorStr += ColorValue(r);
RealStr += r;
}
}
}
return new string[] { ColorStr, RealStr };
}

private int ColorValue(string c)
{
if (c.Contains("Y") || c.Contains("y")) return 1;
if (c.Contains("B") || c.Contains("b")) return 2;
if (c.Contains("R") || c.Contains("r")) return 3;
if (c.Contains("W") || c.Contains("w")) return 4;
if (c.Contains("O") || c.Contains("o")) return 5;
if (c.Contains("G") || c.Contains("g")) return 6;
return 0;
}
```

7，魔方表示法的转换

```//其中s是把6*3*3的数组，用逗号按顺序连接成的字符串
{
string[] ArrColors = s.Split(','); ;
string sInput = "";
string[] PosQ = new string[6];
for (int i = 0; i < 6; i++) PosQ[Convert.ToInt32(ArrColors[4 + i * 9]) - 1] = ReadQ[i].ToString();

sInput += PosQ[Convert.ToInt32(ArrColors[7]) - 1] + PosQ[Convert.ToInt32(ArrColors[37]) - 1] + " ";  //UF
sInput += PosQ[Convert.ToInt32(ArrColors[5]) - 1] + PosQ[Convert.ToInt32(ArrColors[12]) - 1] + " ";  //UR
sInput += PosQ[Convert.ToInt32(ArrColors[1]) - 1] + PosQ[Convert.ToInt32(ArrColors[52]) - 1] + " ";  //UB
sInput += PosQ[Convert.ToInt32(ArrColors[3]) - 1] + PosQ[Convert.ToInt32(ArrColors[32]) - 1] + " ";  //UL
sInput += PosQ[Convert.ToInt32(ArrColors[25]) - 1] + PosQ[Convert.ToInt32(ArrColors[43]) - 1] + " ";  //DF
sInput += PosQ[Convert.ToInt32(ArrColors[21]) - 1] + PosQ[Convert.ToInt32(ArrColors[14]) - 1] + " ";  //DR
sInput += PosQ[Convert.ToInt32(ArrColors[19]) - 1] + PosQ[Convert.ToInt32(ArrColors[46]) - 1] + " ";  //DB
sInput += PosQ[Convert.ToInt32(ArrColors[23]) - 1] + PosQ[Convert.ToInt32(ArrColors[30]) - 1] + " ";  //DL
sInput += PosQ[Convert.ToInt32(ArrColors[41]) - 1] + PosQ[Convert.ToInt32(ArrColors[16]) - 1] + " ";  //FR
sInput += PosQ[Convert.ToInt32(ArrColors[39]) - 1] + PosQ[Convert.ToInt32(ArrColors[34]) - 1] + " ";  //FL
sInput += PosQ[Convert.ToInt32(ArrColors[50]) - 1] + PosQ[Convert.ToInt32(ArrColors[10]) - 1] + " ";  //BR
sInput += PosQ[Convert.ToInt32(ArrColors[48]) - 1] + PosQ[Convert.ToInt32(ArrColors[28]) - 1] + " ";  //BL

sInput += PosQ[Convert.ToInt32(ArrColors[8]) - 1] + PosQ[Convert.ToInt32(ArrColors[38]) - 1] + PosQ[Convert.ToInt32(ArrColors[15]) - 1] + " ";  //UFR
sInput += PosQ[Convert.ToInt32(ArrColors[2]) - 1] + PosQ[Convert.ToInt32(ArrColors[9]) - 1] + PosQ[Convert.ToInt32(ArrColors[53]) - 1] + " ";  //URB
sInput += PosQ[Convert.ToInt32(ArrColors[0]) - 1] + PosQ[Convert.ToInt32(ArrColors[51]) - 1] + PosQ[Convert.ToInt32(ArrColors[29]) - 1] + " ";  //UBL
sInput += PosQ[Convert.ToInt32(ArrColors[6]) - 1] + PosQ[Convert.ToInt32(ArrColors[35]) - 1] + PosQ[Convert.ToInt32(ArrColors[36]) - 1] + " ";  //ULF

sInput += PosQ[Convert.ToInt32(ArrColors[24]) - 1] + PosQ[Convert.ToInt32(ArrColors[17]) - 1] + PosQ[Convert.ToInt32(ArrColors[44]) - 1] + " ";  //DRF
sInput += PosQ[Convert.ToInt32(ArrColors[26]) - 1] + PosQ[Convert.ToInt32(ArrColors[42]) - 1] + PosQ[Convert.ToInt32(ArrColors[33]) - 1] + " ";  //DFL
sInput += PosQ[Convert.ToInt32(ArrColors[20]) - 1] + PosQ[Convert.ToInt32(ArrColors[27]) - 1] + PosQ[Convert.ToInt32(ArrColors[45]) - 1] + " ";  //DLB
sInput += PosQ[Convert.ToInt32(ArrColors[18]) - 1] + PosQ[Convert.ToInt32(ArrColors[47]) - 1] + PosQ[Convert.ToInt32(ArrColors[11]) - 1];  //DBR

ResultSteps = RubikSolve.GetResult(sInput);
}
```

F1 U2 F2 D3 L2 D1 F1 U3 L2 D1

### 解魔方的机器人攻略23 – 识别颜色（上）

1，从NXT发送颜色数据到电脑

```//Send colors to PC
public static void SendColorToPC(int center, int n) throws Exception
{
//get the x,y of n
int y = n % 3;
int x = (n - y) / 3;

//send to PC by bluetooth
byte[] data = new byte[9];
data[0] = (byte)center;    //center表示是魔方的某一面
data[1] = (byte)x;         //x 表示魔方这一面3*3的色块中，第x行的色块
data[2] = (byte)y;         //y 表示魔方这一面3*3的色块中，第y列的色块
data[3] = (byte)color.getRed();
data[4] = (byte)color.getGreen();
data[5] = (byte)color.getBlue();
data[6] = (byte)(color.getRawRed() / 3);
data[7] = (byte)(color.getRawGreen() / 3);
data[8] = (byte)(color.getRawBlue() / 3);
BlueTooth.WriteBytes(data);
}```

2，在PC端接受颜色数据

```else if (length == 9)
{
int i = data[0];
int j = data[1];
int k = data[2];
int r = data[3];
int g = data[4];
int b = data[5];
int rawR = data[6];
int rawG = data[7];
int rawB = data[8];
ColorItem newItem = new ColorItem(i, j, k, r, g, b, rawR, rawG, rawB);
DisplayMessage += newItem.ToString() + "\r\n";
Status = "成功获取数据：" + i + "," + j + "," + k;
}```

3，解析颜色的方案

```Red
[0,1,2]=>RGB=(23,0,0),RawRGB={45,1,8}
[0,2,2]=>RGB=(30,0,0),RawRGB={60,1,5}
[0,2,1]=>RGB=(25,0,0),RawRGB={49,3,12}
[0,2,0]=>RGB=(32,0,0),RawRGB={63,2,6}
[0,1,0]=>RGB=(22,0,0),RawRGB={43,2,11}
[0,0,0]=>RGB=(25,0,0),RawRGB={59,3,3}
[0,0,1]=>RGB=(30,0,0),RawRGB={58,5,17}
[0,0,2]=>RGB=(31,0,0),RawRGB={61,8,17}
[0,1,1]=>RGB=(31,0,0),RawRGB={62,15,22}

Orange
[2,1,2]=>RGB=(28,0,0),RawRGB={55,12,8}
[2,2,1]=>RGB=(30,0,0),RawRGB={57,14,14}
[2,0,1]=>RGB=(32,0,0),RawRGB={62,15,13}
[2,1,0]=>RGB=(32,0,0),RawRGB={63,16,12}
[2,2,2]=>RGB=(42,0,0),RawRGB={83,24,10}
[2,2,0]=>RGB=(41,0,0),RawRGB={82,24,13}
[2,0,0]=>RGB=(41,0,0),RawRGB={80,23,10}
[2,0,2]=>RGB=(39,0,0),RawRGB={76,22,13}
[2,1,1]=>RGB=(41,5,0),RawRGB={81,30,21}```

1，假设RGB三个值的最小值为Min，按Min从大到小排序，前9个是白色
2，剩下的颜色，按照G分量从大到小排序，前9个是黄色（有意思吧，绿色分量最大的是黄色）
3，剩下的颜色，按照B分量从大到小排序，前9个是蓝色（这个还算靠谱）
4，剩下的颜色，按照R分量从小到大排序，前9个是绿色
5，剩下的颜色，按照R+2*RawG-2*RawB从大到小排序，前9个是橙色
6，剩下的颜色全是红色

### 解魔方的机器人攻略21 – 读取魔方颜色

1，依次扫描魔方的6*9=54个色块
2，用蓝牙连接把数据发送到电脑
3，通过颜色分组函数，从读数分辨出不同的颜色

```[0,1,2]=>RGB=(23,0,0),RawRGB={45,1,8}  //红色
[0,2,2]=>RGB=(30,0,0),RawRGB={60,1,5}  //红色
[2,1,2]=>RGB=(28,0,0),RawRGB={55,12,8}  //橙色
[2,2,1]=>RGB=(29,0,0),RawRGB={57,14,14}  //橙色
```

```int[][] idx={
{4,6,7,8,5,2,1,0,3},
{4,0,3,6,7,8,5,2,1},
{4,2,1,0,3,6,7,8,5},
{4,8,5,2,1,0,3,6,7},
{4,2,1,0,3,6,7,8,5},
{4,2,1,0,3,6,7,8,5}};
int[] idx2={5,1,4,3,2,0};```

```	//add offset positions for color sensor motor
static int ColorMotorOffset1 = 33;
static int ColorMotorOffset2 = 9;
static int ColorMotorOffset3 = 18;

//Read each side colors of the cube
{ }

//Read one side by the index
{ }```

### 解魔方的机器人攻略19 – 让魔方动起来

• 按Left键，魔方底座旋转90度
• 按Right键，爪子抓住魔方，然后底座带动最下面的层旋转90度
• 按Enter键，爪子把魔方翻转90度
• 按Escape键，程序退出

1，创建传感器和电机的实例：

```//Define Sensors
static UltrasonicSensor distance=new UltrasonicSensor(SensorPort.S1);
static LightSensor light = new LightSensor(SensorPort.S2);
static ColorSensor color = new ColorSensor(SensorPort.S3);
//Define Motors
static Motor paw=Motor.A;
static Motor monitor=Motor.B;
static Motor bottom=Motor.C;
```

2，创建一个Robot类，这个类用于控制机器人结构上的各种动作，下面三个方法分别对应上面说的三个功能：

```public static class Robot
{
public static void RotateBottomSide(int nQuarter)
{   }

public static void RotateBottom(int nQuarter)
{   }

public static void RotatePaw()throws Exception
{   }
}
```

```//创建实例的用法
Robot instance = new Robot();
instance.rotate();
//静态类的用法
Robot.rotate();
```

3，设置了一些参数

```//如果爪子部分改装了那个3:1的减速齿轮，设置成true，不明白的请看 http://www.diy-robots.com/?p=147 最后两张图
static boolean HasReducer = true;
//爪子抓住魔方时的电机角度
static int PawHoldPosition = 56;
//爪子翻动魔方时的电机角度
static int PawTurnOverPosition = 110;
//底座旋转90时，电机的旋转角度（因为齿轮组的原因）
static int BaseOneQuarter = 315;
//当底座旋转魔方底面时，因为魔方的阻力，需要先多转一个小角度，然后再转回来，这是用来修正误差的角度
static int BaseRotateFix = 40;
```

4，控制电机（motor）的几个函数

```paw.setSpeed(400); //设置转速
paw.rotateTo(nPawHoldPosition); //旋转到一个指定角度（绝对定位）
bottom.rotate(-nFixAngle);    //旋转一定角度（相对定位）
```

5，亮度传感器的一个函数，用于把它的灯打开或者关闭。这里是关闭它，省的晃眼睛，需要的时候再开

```light.setFloodlight(false);
```

http://www.diy-robots.com/RubikSolver/SourceCode/NXT/RubikSolverV2.java_20100115.txt

### 解魔方的机器人攻略18 – 魔方快速算法

U: Up
F: Front
R: Right
L: Left
D: Down
B: Back

1，为什么没有中心的数据？

2，如果是一个打乱的魔方，棱和边的颜色已经和中心不一样了，这时候怎么表示？

FRL之类的字母依然表示六个面，F-表示前层逆时针转90度，U+表示上层顺时针转90度，L2表示左边层转180度。

http://www.diy-robots.com/RubikSolver/RubikSolverSample.zip

Jaap.exe UF UR UB UL DF DR DB DL FR FL BR BL UFR URB UBL ULF DRF DFL DLB DBR

### 解魔方的机器人攻略17 – 魔方CFOP算法

话说曾经有位同事本打算和我一起做萝卜头的，看了这些草图以后，决定还是继续打游戏更靠谱。这不禁让我想起一首歌“1979年，那是一个春天，有一位老人在中国的南海边画了一个圈…” 这位老同志一定是资深软件架构师，改革开放这么宏伟的事情，画个圈就搞定了。

```static String SideColors[] = {
"orgorwwoo",
"oyggbobrg",
"yyrgowwbw",
"yrybgybbo",
"gwwyybror",
"bgrwwrbgy"
```

```public static void RotateBottomSide(boolean ClockWise) throws Exception
{
int temp=0;
int i;

CopyMatrics(2,6,ClockWise?2:1); //Bottom ClockWise = Top Anti-ClockWise
CopyMatrics(6,2,0);
if(ClockWise)
{
for(i=0;i<3;i++)
{
temp=Sides[5][0][i];
Sides[5][0][i]=Sides[3][2-i][0];
Sides[3][2-i][0]=Sides[4][2][2-i];
Sides[4][2][2-i]=Sides[1][i][2];
Sides[1][i][2]=temp;
}
}
else
{
for(i=0;i<3;i++)
{
temp=Sides[5][0][i];
Sides[5][0][i]=Sides[1][i][2];
Sides[1][i][2]=Sides[4][2][2-i];
Sides[4][2][2-i]=Sides[3][2-i][0];
Sides[3][2-i][0]=temp;
}
}
}
```

CFOP解法

CROSS：字面上的意思为“十字”，是Fridrich Method中的第一步骤。
F2L：是“First 2 Layer”的缩写，意思为“一、二层”，是Fridrich Method中的第二步骤。
OLL：是“Orientation of Last Layer”的缩写，意思为“最后一层的角块排序”，这是Fridrich Method中的第三个步骤。
PLL：是“Permutation of Last Layer”的缩写，意思为“最后一层的排序”，这是Fridrich Method中的第四步骤。
CFOP：是Fridrich Method的的别称，就是四个步骤“Cross、F2L、OLL、PLL”原文的第一个字母合起来而成的。

CFOP解法的实现

```TopCross();
TopCorner();
SecondLayer();
BottomCross();
BottomCorner();
ThirdLayerCorner();
ThirdLayerCornerSnap();
ThirdLayerBorderSnap();
```

CFOP算法的源代码可以点这里下载

http://tomas.rokicki.com/cubecontest/ 点最上面的Winners，我用的是第二名的算法。